mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-08 22:51:25 +08:00
完善ubrowser各项参数及UI
This commit is contained in:
parent
c8a9a63dcb
commit
eeda8e66d9
@ -195,6 +195,11 @@ export default {
|
||||
},
|
||||
insertText(text) {
|
||||
this.$refs.editor.repacleEditorSelection(text);
|
||||
this.$refs.editor.formatDocument();
|
||||
},
|
||||
replaceText(text) {
|
||||
this.$refs.editor.setEditorValue(text);
|
||||
this.$refs.editor.formatDocument();
|
||||
},
|
||||
handleComposer({ type, code }) {
|
||||
switch (type) {
|
||||
@ -203,7 +208,7 @@ export default {
|
||||
case "insert":
|
||||
return this.insertText(code);
|
||||
case "apply":
|
||||
return this.$refs.editor.setEditorValue(code);
|
||||
return this.replaceText(code);
|
||||
}
|
||||
},
|
||||
// 保存
|
||||
|
@ -221,10 +221,10 @@ export default {
|
||||
"program-changed",
|
||||
"run",
|
||||
"save",
|
||||
"show-recorder",
|
||||
"show-actions",
|
||||
// "show-recorder",
|
||||
// "show-actions",
|
||||
"show-help",
|
||||
"add-action",
|
||||
"use-composer",
|
||||
],
|
||||
computed: {
|
||||
programLanguages() {
|
||||
|
@ -66,6 +66,16 @@ export default {
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
formatOnType: true,
|
||||
formatOnPaste: true,
|
||||
autoIndent: "full",
|
||||
// JavaScript 特定的格式化选项
|
||||
"javascript.format.insertSpaceAfterSemicolonInForStatements": true,
|
||||
"javascript.format.insertSpaceBeforeAndAfterBinaryOperators": true,
|
||||
"javascript.format.insertSpaceAfterConstructor": true,
|
||||
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets": true,
|
||||
"javascript.format.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": true,
|
||||
"javascript.format.insertSpaceAfterKeywordsInControlFlowStatements": true,
|
||||
};
|
||||
this.editor = monaco.editor.create(
|
||||
document.getElementById("monacoEditor"),
|
||||
@ -212,6 +222,9 @@ export default {
|
||||
};
|
||||
this.rawEditor().executeEdits("my-source", [op]);
|
||||
},
|
||||
formatDocument() {
|
||||
this.rawEditor().getAction("editor.action.formatDocument").run();
|
||||
},
|
||||
listenEditorValue() {
|
||||
this.rawEditor().focus();
|
||||
this.rawEditor().onDidChangeModelContent(() => {
|
||||
|
@ -61,6 +61,7 @@ export default defineComponent({
|
||||
availableCommands,
|
||||
};
|
||||
},
|
||||
emits: ["use-composer", "update:modelValue"],
|
||||
methods: {
|
||||
addCommand(action) {
|
||||
this.commandFlow.push({
|
||||
@ -84,8 +85,9 @@ export default defineComponent({
|
||||
outputVars.set(index, varName);
|
||||
line += `let ${varName} = `;
|
||||
}
|
||||
|
||||
if (cmd.useOutput !== null) {
|
||||
if (cmd.value === "ubrowser") {
|
||||
line += cmd.argv;
|
||||
} else if (cmd.useOutput !== null) {
|
||||
const inputVar = outputVars.get(cmd.useOutput);
|
||||
line += `${cmd.value}(${inputVar})`;
|
||||
} else {
|
||||
|
@ -1,3 +1,9 @@
|
||||
export {
|
||||
ubrowserActionIcons,
|
||||
ubrowserOperationConfigs,
|
||||
defaultUBrowserConfigs,
|
||||
} from "./ubrowser/ubrowserConfig";
|
||||
|
||||
// 定义命令图标映射
|
||||
export const commandIcons = {
|
||||
open: "folder_open",
|
||||
@ -137,58 +143,3 @@ export const commandsAcceptOutput = {
|
||||
send: true,
|
||||
copyTo: true,
|
||||
};
|
||||
|
||||
// 添加 ubrowser 操作图标映射
|
||||
export const ubrowserActionIcons = {
|
||||
wait: "timer",
|
||||
click: "mouse",
|
||||
css: "style",
|
||||
press: "keyboard",
|
||||
paste: "content_paste",
|
||||
screenshot: "photo_camera",
|
||||
pdf: "picture_as_pdf",
|
||||
device: "devices",
|
||||
cookies: "cookie",
|
||||
evaluate: "code",
|
||||
when: "rule",
|
||||
mousedown: "mouse",
|
||||
mouseup: "mouse",
|
||||
file: "upload_file",
|
||||
value: "edit",
|
||||
check: "check_box",
|
||||
focus: "center_focus_strong",
|
||||
scroll: "swap_vert",
|
||||
download: "download",
|
||||
hide: "visibility_off",
|
||||
show: "visibility",
|
||||
devTools: "developer_board",
|
||||
};
|
||||
|
||||
// 添加 ubrowser 可用操作列表
|
||||
export const ubrowserAvailableActions = [
|
||||
{ label: "等待", value: "wait" },
|
||||
{ label: "点击", value: "click" },
|
||||
{ label: "注入CSS", value: "css" },
|
||||
{ label: "按键", value: "press" },
|
||||
{ label: "粘贴", value: "paste" },
|
||||
{ label: "截图", value: "screenshot" },
|
||||
{ label: "导出PDF", value: "pdf" },
|
||||
{ label: "模拟设备", value: "device" },
|
||||
{ label: "获取Cookie", value: "cookies" },
|
||||
{ label: "设置Cookie", value: "setCookies" },
|
||||
{ label: "删除Cookie", value: "removeCookies" },
|
||||
{ label: "清除Cookie", value: "clearCookies" },
|
||||
{ label: "执行脚本", value: "evaluate" },
|
||||
{ label: "条件判断", value: "when" },
|
||||
{ label: "鼠标按下", value: "mousedown" },
|
||||
{ label: "鼠标释放", value: "mouseup" },
|
||||
{ label: "上传文件", value: "file" },
|
||||
{ label: "设置值", value: "value" },
|
||||
{ label: "选中状态", value: "check" },
|
||||
{ label: "获取焦点", value: "focus" },
|
||||
{ label: "滚动", value: "scroll" },
|
||||
{ label: "下载", value: "download" },
|
||||
{ label: "隐藏", value: "hide" },
|
||||
{ label: "显示", value: "show" },
|
||||
{ label: "开发工具", value: "devTools" },
|
||||
];
|
||||
|
@ -15,22 +15,6 @@
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<!-- 超时配置 -->
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
v-model.number="localConfigs.goto.timeout"
|
||||
type="number"
|
||||
label="超时时间(ms)"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="timer" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<!-- Headers配置 -->
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
@ -48,20 +32,57 @@
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
v-model="localConfigs.goto.headers.userAgent"
|
||||
label="User-Agent"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="computer" />
|
||||
</template>
|
||||
</q-input>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="localConfigs.goto.headers.userAgent"
|
||||
label="User-Agent"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="devices" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-select
|
||||
v-model="selectedUA"
|
||||
:options="userAgentOptions"
|
||||
label="常用 UA"
|
||||
dense
|
||||
outlined
|
||||
emit-value
|
||||
map-options
|
||||
options-dense
|
||||
style="min-width: 150px"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="list" />
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 超时配置 -->
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
v-model.number="localConfigs.goto.timeout"
|
||||
type="number"
|
||||
label="超时时间(ms)"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="timer" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -76,10 +97,14 @@ export default defineComponent({
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:configs"],
|
||||
data() {
|
||||
return {
|
||||
selectedUA: null,
|
||||
localConfigs: {
|
||||
useragent: {
|
||||
preset: null,
|
||||
value: "",
|
||||
},
|
||||
goto: {
|
||||
url: "",
|
||||
headers: {
|
||||
@ -89,27 +114,83 @@ export default defineComponent({
|
||||
timeout: 60000,
|
||||
},
|
||||
},
|
||||
userAgentOptions: [
|
||||
{
|
||||
label: "Chrome (Windows)",
|
||||
value:
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
||||
},
|
||||
{
|
||||
label: "Chrome (macOS)",
|
||||
value:
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
||||
},
|
||||
{
|
||||
label: "Chrome (Linux)",
|
||||
value:
|
||||
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
||||
},
|
||||
{
|
||||
label: "IE 11",
|
||||
value:
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko",
|
||||
},
|
||||
{
|
||||
label: "微信 (Android)",
|
||||
value:
|
||||
"Mozilla/5.0 (Linux; Android 14; Pixel 8 Build/UQ1A.240205.002; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/122.0.6261.64 Mobile Safari/537.36 XWEB/1160027 MMWEBSDK/20231202 MMWEBID/2308 MicroMessenger/8.0.47.2560(0x28002F35) WeChat/arm64 Weixin NetType/WIFI Language/zh_CN ABI/arm64",
|
||||
},
|
||||
{
|
||||
label: "微信 (iOS)",
|
||||
value:
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.47(0x18002f2c) NetType/WIFI Language/zh_CN",
|
||||
},
|
||||
{
|
||||
label: "iPhone",
|
||||
value:
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
|
||||
},
|
||||
{
|
||||
label: "iPad",
|
||||
value:
|
||||
"Mozilla/5.0 (iPad; CPU OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
|
||||
},
|
||||
{
|
||||
label: "Android Phone",
|
||||
value:
|
||||
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
|
||||
},
|
||||
{
|
||||
label: "Android Tablet",
|
||||
value:
|
||||
"Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 初始化本地配置
|
||||
this.localConfigs = JSON.parse(JSON.stringify(this.configs));
|
||||
this.localConfigs = _.cloneDeep(this.configs);
|
||||
},
|
||||
methods: {
|
||||
updateConfigs() {
|
||||
this.$emit(
|
||||
"update:configs",
|
||||
JSON.parse(JSON.stringify(this.localConfigs))
|
||||
);
|
||||
this.$emit("update:configs", _.cloneDeep(this.localConfigs));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
configs: {
|
||||
deep: true,
|
||||
handler(newConfigs) {
|
||||
this.localConfigs = JSON.parse(JSON.stringify(newConfigs));
|
||||
this.localConfigs = _.cloneDeep(newConfigs);
|
||||
},
|
||||
},
|
||||
selectedUA(value) {
|
||||
if (value) {
|
||||
this.localConfigs.goto.headers.userAgent = value;
|
||||
this.updateConfigs();
|
||||
this.selectedUA = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -66,6 +66,8 @@ import { defineComponent } from "vue";
|
||||
import UBrowserBasic from "./UBrowserBasic.vue";
|
||||
import UBrowserOperations from "./UBrowserOperations.vue";
|
||||
import UBrowserRun from "./UBrowserRun.vue";
|
||||
import { defaultUBrowserConfigs } from "./ubrowserConfig";
|
||||
import { generateUBrowserCode } from "./generateUBrowserCode";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserEditor",
|
||||
@ -85,142 +87,7 @@ export default defineComponent({
|
||||
return {
|
||||
step: 1,
|
||||
selectedActions: [],
|
||||
configs: {
|
||||
// 基础参数
|
||||
useragent: {
|
||||
value: "",
|
||||
},
|
||||
goto: {
|
||||
url: "",
|
||||
headers: {
|
||||
Referer: "",
|
||||
userAgent: "",
|
||||
},
|
||||
timeout: 60000,
|
||||
},
|
||||
// 浏览器操作
|
||||
wait: {
|
||||
value: "",
|
||||
timeout: 60000,
|
||||
},
|
||||
click: {
|
||||
selector: "",
|
||||
},
|
||||
css: {
|
||||
value: "",
|
||||
},
|
||||
press: {
|
||||
key: "",
|
||||
modifiers: [],
|
||||
},
|
||||
paste: {
|
||||
text: "",
|
||||
},
|
||||
screenshot: {
|
||||
selector: "",
|
||||
rect: { x: 0, y: 0, width: 0, height: 0 },
|
||||
savePath: "",
|
||||
},
|
||||
pdf: {
|
||||
options: {
|
||||
marginsType: 0,
|
||||
pageSize: "A4",
|
||||
},
|
||||
savePath: "",
|
||||
},
|
||||
device: {
|
||||
size: { width: 1280, height: 800 },
|
||||
useragent: "",
|
||||
},
|
||||
cookies: {
|
||||
name: "",
|
||||
},
|
||||
setCookies: {
|
||||
items: [{ name: "", value: "" }],
|
||||
},
|
||||
removeCookies: {
|
||||
name: "",
|
||||
},
|
||||
clearCookies: {
|
||||
url: "",
|
||||
},
|
||||
evaluate: {
|
||||
function: "",
|
||||
params: [],
|
||||
},
|
||||
when: {
|
||||
condition: "",
|
||||
},
|
||||
mousedown: {
|
||||
selector: "",
|
||||
},
|
||||
mouseup: {
|
||||
selector: "",
|
||||
},
|
||||
file: {
|
||||
selector: "",
|
||||
files: [],
|
||||
},
|
||||
value: {
|
||||
selector: "",
|
||||
value: "",
|
||||
},
|
||||
check: {
|
||||
selector: "",
|
||||
checked: false,
|
||||
},
|
||||
focus: {
|
||||
selector: "",
|
||||
},
|
||||
scroll: {
|
||||
target: "",
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
download: {
|
||||
url: "",
|
||||
savePath: "",
|
||||
},
|
||||
// 运行参数
|
||||
run: {
|
||||
show: true,
|
||||
width: 1280,
|
||||
height: 800,
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
center: true,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
maxWidth: undefined,
|
||||
maxHeight: undefined,
|
||||
resizable: true,
|
||||
movable: true,
|
||||
minimizable: true,
|
||||
maximizable: true,
|
||||
alwaysOnTop: false,
|
||||
fullscreen: false,
|
||||
fullscreenable: true,
|
||||
enableLargerThanScreen: false,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
defaultRunConfigs: {
|
||||
show: true,
|
||||
width: 1280,
|
||||
height: 800,
|
||||
center: true,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
resizable: true,
|
||||
movable: true,
|
||||
minimizable: true,
|
||||
maximizable: true,
|
||||
alwaysOnTop: false,
|
||||
fullscreen: false,
|
||||
fullscreenable: true,
|
||||
enableLargerThanScreen: false,
|
||||
opacity: 1,
|
||||
},
|
||||
configs: _.cloneDeep(defaultUBrowserConfigs),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -235,249 +102,31 @@ export default defineComponent({
|
||||
this.selectedActions.splice(index, 1);
|
||||
}
|
||||
},
|
||||
generateCode() {
|
||||
let code = "utools.ubrowser";
|
||||
|
||||
// 基础参数
|
||||
if (this.configs.useragent.value) {
|
||||
code += `.useragent('${this.configs.useragent.value}')`;
|
||||
}
|
||||
|
||||
if (this.configs.goto.url) {
|
||||
const gotoOptions = {};
|
||||
if (this.configs.goto.headers.Referer) {
|
||||
gotoOptions.headers = gotoOptions.headers || {};
|
||||
gotoOptions.headers.Referer = this.configs.goto.headers.Referer;
|
||||
}
|
||||
if (this.configs.goto.headers.userAgent) {
|
||||
gotoOptions.headers = gotoOptions.headers || {};
|
||||
gotoOptions.headers["User-Agent"] =
|
||||
this.configs.goto.headers.userAgent;
|
||||
}
|
||||
if (this.configs.goto.timeout !== 60000) {
|
||||
gotoOptions.timeout = this.configs.goto.timeout;
|
||||
}
|
||||
|
||||
code += `.goto('${this.configs.goto.url}'${
|
||||
Object.keys(gotoOptions).length
|
||||
? `, ${JSON.stringify(gotoOptions)}`
|
||||
: ""
|
||||
})`;
|
||||
}
|
||||
|
||||
// 浏览器操作
|
||||
this.selectedActions.forEach((action) => {
|
||||
const config = this.configs[action.value];
|
||||
switch (action.value) {
|
||||
case "wait":
|
||||
if (config.value) {
|
||||
code += `.wait('${config.value}'${
|
||||
config.timeout !== 60000 ? `, ${config.timeout}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "click":
|
||||
if (config.selector) {
|
||||
code += `.click('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "css":
|
||||
if (config.value) {
|
||||
code += `.css('${config.value}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "press":
|
||||
if (config.key) {
|
||||
const modifiers = config.modifiers.length
|
||||
? `, ${JSON.stringify(config.modifiers)}`
|
||||
: "";
|
||||
code += `.press('${config.key}'${modifiers})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "paste":
|
||||
if (config.text) {
|
||||
code += `.paste('${config.text}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "screenshot":
|
||||
if (config.selector || config.savePath) {
|
||||
const options = {};
|
||||
if (config.selector) options.selector = config.selector;
|
||||
if (config.rect.width && config.rect.height) {
|
||||
options.rect = config.rect;
|
||||
}
|
||||
code += `.screenshot('${config.savePath}'${
|
||||
Object.keys(options).length
|
||||
? `, ${JSON.stringify(options)}`
|
||||
: ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "pdf":
|
||||
if (config.savePath) {
|
||||
code += `.pdf('${config.savePath}'${
|
||||
config.options ? `, ${JSON.stringify(config.options)}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "device":
|
||||
if (config.size.width && config.size.height) {
|
||||
const options = {
|
||||
size: config.size,
|
||||
};
|
||||
if (config.useragent) options.useragent = config.useragent;
|
||||
code += `.device(${JSON.stringify(options)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "cookies":
|
||||
if (config.name) {
|
||||
code += `.cookies('${config.name}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "setCookies":
|
||||
if (config.items?.length) {
|
||||
code += `.setCookies(${JSON.stringify(config.items)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "removeCookies":
|
||||
if (config.name) {
|
||||
code += `.removeCookies('${config.name}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "clearCookies":
|
||||
code += `.clearCookies(${config.url ? `'${config.url}'` : ""})`;
|
||||
break;
|
||||
|
||||
case "evaluate":
|
||||
if (config.function) {
|
||||
const params = config.params.length
|
||||
? `, ${JSON.stringify(config.params)}`
|
||||
: "";
|
||||
code += `.evaluate(\`${config.function}\`${params})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "when":
|
||||
if (config.condition) {
|
||||
code += `.when('${config.condition}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "mousedown":
|
||||
case "mouseup":
|
||||
if (config.selector) {
|
||||
code += `.${action.value}('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "file":
|
||||
if (config.selector && config.files?.length) {
|
||||
code += `.file('${config.selector}', ${JSON.stringify(
|
||||
config.files
|
||||
)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "value":
|
||||
if (config.selector) {
|
||||
code += `.value('${config.selector}', '${config.value}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "check":
|
||||
if (config.selector) {
|
||||
code += `.check('${config.selector}'${
|
||||
config.checked !== undefined ? `, ${config.checked}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "focus":
|
||||
if (config.selector) {
|
||||
code += `.focus('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "scroll":
|
||||
if (config.type === "element" && config.selector) {
|
||||
code += `.scroll('${config.selector}')`;
|
||||
} else if (config.type === "position") {
|
||||
if (config.x !== undefined && config.y !== undefined) {
|
||||
code += `.scroll(${config.x}, ${config.y})`;
|
||||
} else if (config.y !== undefined) {
|
||||
code += `.scroll(${config.y})`;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "download":
|
||||
if (config.url) {
|
||||
code += `.download('${config.url}'${
|
||||
config.savePath ? `, '${config.savePath}'` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "hide":
|
||||
case "show":
|
||||
code += `.${action.value}()`;
|
||||
break;
|
||||
|
||||
case "devTools":
|
||||
if (config.mode) {
|
||||
code += `.devTools('${config.mode}')`;
|
||||
} else {
|
||||
code += `.devTools()`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 运行参数
|
||||
const runOptions = {};
|
||||
Object.entries(this.configs.run).forEach(([key, value]) => {
|
||||
if (
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
value !== this.defaultRunConfigs[key]
|
||||
) {
|
||||
runOptions[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
code += `.run(${
|
||||
Object.keys(runOptions).length ? JSON.stringify(runOptions) : ""
|
||||
})`;
|
||||
|
||||
this.$emit("update:modelValue", code);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
configs: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.generateCode();
|
||||
this.$emit(
|
||||
"update:modelValue",
|
||||
generateUBrowserCode(this.configs, this.selectedActions)
|
||||
);
|
||||
},
|
||||
},
|
||||
selectedActions: {
|
||||
handler() {
|
||||
this.generateCode();
|
||||
this.$emit(
|
||||
"update:modelValue",
|
||||
generateUBrowserCode(this.configs, this.selectedActions)
|
||||
);
|
||||
},
|
||||
},
|
||||
step: {
|
||||
handler() {
|
||||
this.generateCode();
|
||||
this.$emit(
|
||||
"update:modelValue",
|
||||
generateUBrowserCode(this.configs, this.selectedActions)
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -4,8 +4,10 @@
|
||||
<!-- 操作选择网格 -->
|
||||
<div class="row q-col-gutter-xs">
|
||||
<div
|
||||
v-for="action in availableActions"
|
||||
:key="action.value"
|
||||
v-for="[actionName, { label }] in Object.entries(
|
||||
ubrowserOperationConfigs
|
||||
)"
|
||||
:key="actionName"
|
||||
class="col-2"
|
||||
>
|
||||
<q-card
|
||||
@ -14,13 +16,13 @@
|
||||
class="action-card cursor-pointer"
|
||||
:class="{
|
||||
'action-selected': selectedActions.some(
|
||||
(a) => a.value === action.value
|
||||
(a) => a.value === actionName
|
||||
),
|
||||
}"
|
||||
@click="toggleAction(action)"
|
||||
@click="toggleAction({ value: actionName, label: label })"
|
||||
>
|
||||
<div class="q-pa-xs text-caption text-wrap text-center">
|
||||
{{ action.label }}
|
||||
{{ label }}
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
@ -43,7 +45,9 @@
|
||||
<q-avatar color="primary">
|
||||
<q-icon
|
||||
color="white"
|
||||
:name="getActionIcon(action.value)"
|
||||
:name="
|
||||
ubrowserOperationConfigs[action.value].icon || 'touch_app'
|
||||
"
|
||||
size="14px"
|
||||
/>
|
||||
</q-avatar>
|
||||
@ -72,11 +76,11 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="getOperationConfig(action.value)">
|
||||
<div v-if="ubrowserOperationConfigs[action.value].config">
|
||||
<UBrowserOperation
|
||||
:configs="configs"
|
||||
:action="action.value"
|
||||
:fields="getOperationConfig(action.value)"
|
||||
:fields="ubrowserOperationConfigs[action.value].config"
|
||||
@update:configs="$emit('update:configs', $event)"
|
||||
/>
|
||||
</div>
|
||||
@ -88,10 +92,7 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import {
|
||||
ubrowserActionIcons,
|
||||
ubrowserAvailableActions,
|
||||
} from "../composerConfig";
|
||||
import { ubrowserOperationConfigs } from "../composerConfig";
|
||||
import UBrowserOperation from "./operations/UBrowserOperation.vue";
|
||||
|
||||
export default defineComponent({
|
||||
@ -109,12 +110,12 @@ export default defineComponent({
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["remove-action", "update:selectedActions", "update:configs"],
|
||||
computed: {
|
||||
availableActions() {
|
||||
return ubrowserAvailableActions;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ubrowserOperationConfigs: ubrowserOperationConfigs,
|
||||
};
|
||||
},
|
||||
emits: ["remove-action", "update:selectedActions", "update:configs"],
|
||||
methods: {
|
||||
moveAction(index, direction) {
|
||||
const newIndex = index + direction;
|
||||
@ -126,341 +127,6 @@ export default defineComponent({
|
||||
this.$emit("update:selectedActions", actions);
|
||||
}
|
||||
},
|
||||
getActionIcon(action) {
|
||||
return ubrowserActionIcons[action] || "touch_app";
|
||||
},
|
||||
getOperationConfig(action) {
|
||||
const configs = {
|
||||
wait: [
|
||||
{
|
||||
key: "value",
|
||||
label: "等待时间(ms)或CSS选择器",
|
||||
icon: "timer",
|
||||
type: "input",
|
||||
width: 8,
|
||||
},
|
||||
{
|
||||
key: "timeout",
|
||||
label: "超时时间(ms)",
|
||||
icon: "timer_off",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 4,
|
||||
},
|
||||
],
|
||||
click: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "点击元素的CSS选择器",
|
||||
icon: "mouse",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
css: [
|
||||
{
|
||||
key: "value",
|
||||
label: "注入的CSS样式",
|
||||
icon: "style",
|
||||
type: "textarea",
|
||||
},
|
||||
],
|
||||
press: [
|
||||
{
|
||||
key: "key",
|
||||
label: "按键",
|
||||
icon: "keyboard",
|
||||
type: "input",
|
||||
width: 5,
|
||||
},
|
||||
{
|
||||
key: "modifiers",
|
||||
label: "修饰键",
|
||||
type: "checkbox-group",
|
||||
options: [
|
||||
{ label: "Ctrl", value: "ctrl" },
|
||||
{ label: "Shift", value: "shift" },
|
||||
{ label: "Alt", value: "alt" },
|
||||
{ label: "Meta", value: "meta" },
|
||||
],
|
||||
defaultValue: [],
|
||||
width: 7,
|
||||
},
|
||||
],
|
||||
paste: [
|
||||
{
|
||||
key: "text",
|
||||
label: "粘贴内容",
|
||||
icon: "content_paste",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
viewport: [
|
||||
{
|
||||
key: "width",
|
||||
label: "视窗宽度",
|
||||
icon: "width",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "height",
|
||||
label: "视窗高度",
|
||||
icon: "height",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
},
|
||||
],
|
||||
screenshot: [
|
||||
{ key: "selector", label: "元素选择器", icon: "crop", type: "input" },
|
||||
{
|
||||
key: "rect.x",
|
||||
label: "X坐标",
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.y",
|
||||
label: "Y坐标",
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.width",
|
||||
label: "宽度",
|
||||
icon: "width",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.height",
|
||||
label: "高度",
|
||||
icon: "height",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 3,
|
||||
},
|
||||
{ key: "savePath", label: "保存路径", icon: "save", type: "input" },
|
||||
],
|
||||
pdf: [
|
||||
{
|
||||
key: "options.marginsType",
|
||||
label: "边距类型",
|
||||
type: "select",
|
||||
options: [
|
||||
{ label: "默认边距", value: 0 },
|
||||
{ label: "无边距", value: 1 },
|
||||
{ label: "最小边距", value: 2 },
|
||||
],
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "options.pageSize",
|
||||
label: "页面大小",
|
||||
type: "select",
|
||||
options: ["A3", "A4", "A5", "Legal", "Letter", "Tabloid"],
|
||||
width: 6,
|
||||
},
|
||||
{ key: "savePath", label: "保存路径", icon: "save", type: "input" },
|
||||
],
|
||||
device: [
|
||||
{
|
||||
key: "size.width",
|
||||
label: "设备宽度",
|
||||
icon: "width",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "size.height",
|
||||
label: "设备高度",
|
||||
icon: "height",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "useragent",
|
||||
label: "设备User-Agent",
|
||||
icon: "phone_android",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
cookies: [
|
||||
{ key: "name", label: "Cookie名称", icon: "cookie", type: "input" },
|
||||
],
|
||||
setCookies: [
|
||||
{ key: "items", label: "Cookie列表", type: "cookie-list" },
|
||||
],
|
||||
removeCookies: [
|
||||
{ key: "name", label: "Cookie名称", icon: "cookie", type: "input" },
|
||||
],
|
||||
clearCookies: [
|
||||
{ key: "url", label: "URL(可选)", icon: "link", type: "input" },
|
||||
],
|
||||
evaluate: [
|
||||
{
|
||||
key: "function",
|
||||
label: "JavaScript代码",
|
||||
icon: "code",
|
||||
type: "textarea",
|
||||
},
|
||||
{ key: "params", label: "参数列表", type: "param-list" },
|
||||
],
|
||||
when: [
|
||||
{
|
||||
key: "condition",
|
||||
label: "条件(JavaScript表达式或选择器)",
|
||||
icon: "rule",
|
||||
type: "textarea",
|
||||
},
|
||||
],
|
||||
mousedown: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "按下元素选择器",
|
||||
icon: "mouse",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
mouseup: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "释放元素选择器",
|
||||
icon: "mouse",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
file: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "文件输入框选择器",
|
||||
icon: "upload_file",
|
||||
type: "input",
|
||||
},
|
||||
{ key: "files", label: "文件列表", type: "file-list", width: 12 },
|
||||
],
|
||||
value: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "元素选择器",
|
||||
icon: "input",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "value",
|
||||
label: "设置的值",
|
||||
icon: "edit",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
],
|
||||
check: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "复选框/选框选择器",
|
||||
icon: "check_box",
|
||||
type: "input",
|
||||
width: 8,
|
||||
},
|
||||
{
|
||||
key: "checked",
|
||||
label: "选中状态",
|
||||
type: "checkbox",
|
||||
defaultValue: false,
|
||||
width: 4,
|
||||
},
|
||||
],
|
||||
focus: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "元素选择器",
|
||||
icon: "center_focus_strong",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
scroll: [
|
||||
{
|
||||
key: "type",
|
||||
label: "滚动类型",
|
||||
type: "button-toggle",
|
||||
options: [
|
||||
{ label: "滚动到元素", value: "element" },
|
||||
{ label: "滚动到坐标", value: "position" },
|
||||
],
|
||||
defaultValue: "element",
|
||||
},
|
||||
{
|
||||
key: "selector",
|
||||
label: "目标元素选择器",
|
||||
icon: "swap_vert",
|
||||
type: "input",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "element",
|
||||
},
|
||||
{
|
||||
key: "x",
|
||||
label: "X坐标",
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
showWhen: "type",
|
||||
showValue: "position",
|
||||
},
|
||||
{
|
||||
key: "y",
|
||||
label: "Y坐标",
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
showWhen: "type",
|
||||
showValue: "position",
|
||||
},
|
||||
],
|
||||
download: [
|
||||
{
|
||||
key: "url",
|
||||
label: "下载URL",
|
||||
icon: "link",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "savePath",
|
||||
label: "保存路径",
|
||||
icon: "save",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
],
|
||||
devTools: [
|
||||
{
|
||||
key: "mode",
|
||||
label: "开发工具位置",
|
||||
type: "button-toggle",
|
||||
options: [
|
||||
{ label: "右侧", value: "right" },
|
||||
{ label: "底部", value: "bottom" },
|
||||
{ label: "独立", value: "undocked" },
|
||||
{ label: "分离", value: "detach" },
|
||||
],
|
||||
defaultValue: "right",
|
||||
},
|
||||
],
|
||||
};
|
||||
return configs[action];
|
||||
},
|
||||
toggleAction(action) {
|
||||
const index = this.selectedActions.findIndex(
|
||||
(a) => a.value === action.value
|
||||
@ -481,7 +147,7 @@ export default defineComponent({
|
||||
]);
|
||||
|
||||
// 初始化配置对象
|
||||
const config = this.getOperationConfig(action.value);
|
||||
const { config } = this.ubrowserOperationConfigs[action.value];
|
||||
if (config) {
|
||||
const newConfigs = { ...this.configs };
|
||||
if (!newConfigs[action.value]) {
|
||||
|
@ -198,49 +198,20 @@ export default defineComponent({
|
||||
emits: ["update:configs"],
|
||||
data() {
|
||||
return {
|
||||
localConfigs: {
|
||||
run: {
|
||||
show: true,
|
||||
width: 1280,
|
||||
height: 800,
|
||||
x: undefined,
|
||||
y: undefined,
|
||||
center: true,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
maxWidth: undefined,
|
||||
maxHeight: undefined,
|
||||
resizable: true,
|
||||
movable: true,
|
||||
minimizable: true,
|
||||
maximizable: true,
|
||||
alwaysOnTop: false,
|
||||
fullscreen: false,
|
||||
fullscreenable: true,
|
||||
enableLargerThanScreen: false,
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
localConfigs: _.cloneDeep(this.configs),
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 初始化本地配置
|
||||
this.localConfigs = JSON.parse(JSON.stringify(this.configs));
|
||||
},
|
||||
methods: {
|
||||
updateConfig(key, value) {
|
||||
this.localConfigs.run[key] = value;
|
||||
this.$emit(
|
||||
"update:configs",
|
||||
JSON.parse(JSON.stringify(this.localConfigs))
|
||||
);
|
||||
this.$emit("update:configs", _.cloneDeep(this.localConfigs));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
configs: {
|
||||
deep: true,
|
||||
handler(newConfigs) {
|
||||
this.localConfigs = JSON.parse(JSON.stringify(newConfigs));
|
||||
this.localConfigs = _.cloneDeep(newConfigs);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
264
src/components/editor/composer/ubrowser/generateUBrowserCode.js
Normal file
264
src/components/editor/composer/ubrowser/generateUBrowserCode.js
Normal file
@ -0,0 +1,264 @@
|
||||
/**
|
||||
* 生成 UBrowser 代码
|
||||
* @param {Object} configs UBrowser 配置对象
|
||||
* @param {Array} selectedActions 已选择的操作列表
|
||||
* @returns {string} 生成的代码
|
||||
*/
|
||||
import { defaultUBrowserConfigs } from "./ubrowserConfig";
|
||||
|
||||
export function generateUBrowserCode(configs, selectedActions) {
|
||||
let code = "utools.ubrowser";
|
||||
|
||||
// 基础参数
|
||||
if (configs.useragent.value) {
|
||||
code += `\n .useragent('${configs.useragent.value}')`;
|
||||
}
|
||||
|
||||
if (configs.goto.url) {
|
||||
const gotoOptions = {};
|
||||
if (configs.goto.headers.Referer) {
|
||||
gotoOptions.headers = gotoOptions.headers || {};
|
||||
gotoOptions.headers.Referer = configs.goto.headers.Referer;
|
||||
}
|
||||
if (configs.goto.headers.userAgent) {
|
||||
gotoOptions.headers = gotoOptions.headers || {};
|
||||
gotoOptions.headers["User-Agent"] = configs.goto.headers.userAgent;
|
||||
}
|
||||
if (configs.goto.timeout !== 60000) {
|
||||
gotoOptions.timeout = configs.goto.timeout;
|
||||
}
|
||||
|
||||
code += `\n .goto('${configs.goto.url}'${
|
||||
Object.keys(gotoOptions).length
|
||||
? `,\n${JSON.stringify(gotoOptions, null, 2).replace(/\n/g, "\n ")}`
|
||||
: ""
|
||||
})`;
|
||||
}
|
||||
|
||||
// 浏览器操作
|
||||
selectedActions.forEach((action) => {
|
||||
const config = configs[action.value];
|
||||
switch (action.value) {
|
||||
case "wait":
|
||||
if (config.type === "time" && config.time) {
|
||||
code += `\n .wait(${config.time})`;
|
||||
} else if (config.type === "selector" && config.selector) {
|
||||
code += `\n .wait('${config.selector}'${
|
||||
config.timeout !== 60000 ? `, ${config.timeout}` : ""
|
||||
})`;
|
||||
} else if (config.type === "function" && config.function) {
|
||||
const functionBody = config.function.trim();
|
||||
if (config.args?.length) {
|
||||
const params = config.args.map((arg) => arg.name).join(", ");
|
||||
const functionCode = `(${params}) => {\n ${functionBody} \n}`;
|
||||
const args = `, ${config.timeout || 60000}, ${config.args
|
||||
.map((arg) => JSON.stringify(arg.value))
|
||||
.join(", ")}`;
|
||||
code += `\n .wait(${functionCode}${args})`;
|
||||
} else {
|
||||
const functionCode = `() => {\n ${functionBody} \n}`;
|
||||
code += `\n .wait(${functionCode}${
|
||||
config.timeout !== 60000 ? `, ${config.timeout}` : ""
|
||||
})`;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "click":
|
||||
if (config.selector) {
|
||||
code += `\n .click('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "css":
|
||||
if (config.value) {
|
||||
code += `\n .css('${config.value}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "press":
|
||||
if (config.key) {
|
||||
const modifiers = config.modifiers.length
|
||||
? `, ${JSON.stringify(config.modifiers)}`
|
||||
: "";
|
||||
code += `\n .press('${config.key}'${modifiers})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "paste":
|
||||
if (config.text) {
|
||||
code += `\n .paste('${config.text}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "screenshot":
|
||||
if (config.selector || config.savePath) {
|
||||
const options = {};
|
||||
if (config.selector) options.selector = config.selector;
|
||||
if (config.rect.width && config.rect.height) {
|
||||
options.rect = config.rect;
|
||||
}
|
||||
code += `\n .screenshot('${config.savePath}'${
|
||||
Object.keys(options).length ? `, ${JSON.stringify(options)}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "pdf":
|
||||
if (config.savePath) {
|
||||
code += `\n .pdf('${config.savePath}'${
|
||||
config.options ? `, ${JSON.stringify(config.options)}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "device":
|
||||
if (config.type === "preset" && config.deviceName) {
|
||||
code += `\n .device('${config.deviceName}')`;
|
||||
} else if (config.type === "custom") {
|
||||
const options = {
|
||||
size: config.size,
|
||||
};
|
||||
if (config.useragent) options.useragent = config.useragent;
|
||||
code += `\n .device(${JSON.stringify(options, null, 2).replace(
|
||||
/\n/g,
|
||||
"\n "
|
||||
)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "cookies":
|
||||
if (config.name) {
|
||||
code += `\n .cookies('${config.name}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "setCookies":
|
||||
if (config.items?.length) {
|
||||
code += `\n .setCookies(${JSON.stringify(config.items)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "removeCookies":
|
||||
if (config.name) {
|
||||
code += `\n .removeCookies('${config.name}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "clearCookies":
|
||||
code += `\n .clearCookies(${config.url ? `'${config.url}'` : ""})`;
|
||||
break;
|
||||
|
||||
case "evaluate":
|
||||
if (config.function) {
|
||||
const functionBody = config.function.trim();
|
||||
if (config.args?.length) {
|
||||
const params = config.args.map((arg) => arg.name).join(", ");
|
||||
const functionCode = `(${params}) => {\n ${functionBody} \n}`;
|
||||
const args = `, ${config.args
|
||||
.map((arg) => JSON.stringify(arg.value))
|
||||
.join(", ")}`;
|
||||
code += `\n .evaluate(${functionCode}${args})`;
|
||||
} else {
|
||||
const functionCode = `() => {\n ${functionBody} \n}`;
|
||||
code += `\n .evaluate(${functionCode})`;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "when":
|
||||
if (config.condition) {
|
||||
code += `\n .when('${config.condition}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "mousedown":
|
||||
case "mouseup":
|
||||
if (config.selector) {
|
||||
code += `\n .${action.value}('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "file":
|
||||
if (config.selector && config.files?.length) {
|
||||
code += `\n .file('${config.selector}', ${JSON.stringify(
|
||||
config.files
|
||||
)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "value":
|
||||
if (config.selector) {
|
||||
code += `\n .value('${config.selector}', '${config.value}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "check":
|
||||
if (config.selector) {
|
||||
code += `\n .check('${config.selector}'${
|
||||
config.checked !== undefined ? `, ${config.checked}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "focus":
|
||||
if (config.selector) {
|
||||
code += `\n .focus('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "scroll":
|
||||
if (config.type === "element" && config.selector) {
|
||||
code += `\n .scroll('${config.selector}')`;
|
||||
} else if (config.type === "position") {
|
||||
if (config.x !== undefined && config.y !== undefined) {
|
||||
code += `\n .scroll(${config.x}, ${config.y})`;
|
||||
} else if (config.y !== undefined) {
|
||||
code += `\n .scroll(${config.y})`;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "download":
|
||||
if (config.url) {
|
||||
code += `\n .download('${config.url}'${
|
||||
config.savePath ? `, '${config.savePath}'` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "hide":
|
||||
case "show":
|
||||
code += `\n .${action.value}()`;
|
||||
break;
|
||||
|
||||
case "devTools":
|
||||
if (config.mode) {
|
||||
code += `\n .devTools('${config.mode}')`;
|
||||
} else {
|
||||
code += `\n .devTools()`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// 运行参数
|
||||
const runOptions = {};
|
||||
Object.entries(configs.run).forEach(([key, value]) => {
|
||||
if (
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
value !== defaultUBrowserConfigs.run[key]
|
||||
) {
|
||||
runOptions[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
code += `\n .run(${
|
||||
Object.keys(runOptions).length
|
||||
? `\n${JSON.stringify(runOptions, null, 2).replace(/\n/g, "\n ")}`
|
||||
: ""
|
||||
})`;
|
||||
|
||||
return code;
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="row items-center no-wrap">
|
||||
<q-badge class="q-pa-xs">{{ label }}</q-badge>
|
||||
<q-btn-toggle
|
||||
:model-value="modelValue"
|
||||
:options="options"
|
||||
dense
|
||||
flat
|
||||
no-caps
|
||||
spread
|
||||
class="button-group"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserButtonToggle",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number, Boolean],
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-group {
|
||||
flex: 1;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-btn) {
|
||||
min-height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div class="row items-center no-wrap">
|
||||
<q-badge class="q-pa-xs">{{ label }}</q-badge>
|
||||
<q-btn-toggle
|
||||
:model-value="modelValue ? 'true' : 'false'"
|
||||
:options="[
|
||||
{ label: '是', value: 'true' },
|
||||
{ label: '否', value: 'false' },
|
||||
]"
|
||||
dense
|
||||
flat
|
||||
no-caps
|
||||
spread
|
||||
class="button-group"
|
||||
@update:model-value="$emit('update:modelValue', $event === 'true')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserCheckbox",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
});
|
||||
</script>
|
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="row items-center">
|
||||
<q-option-group
|
||||
:model-value="modelValue"
|
||||
:options="options"
|
||||
type="checkbox"
|
||||
inline
|
||||
dense
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserCheckboxGroup",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
});
|
||||
</script>
|
@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div
|
||||
v-for="(cookie, index) in modelValue || [{}]"
|
||||
:key="index"
|
||||
class="col-12"
|
||||
>
|
||||
<div class="row items-center q-gutter-x-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
:model-value="cookie.name"
|
||||
label="名称"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="
|
||||
(value) => handleUpdate(index, 'name', value)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
:model-value="cookie.value"
|
||||
label="值"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="
|
||||
(value) => handleUpdate(index, 'value', value)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="negative"
|
||||
icon="remove"
|
||||
@click="removeCookie(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
color="primary"
|
||||
icon="add"
|
||||
label="添加Cookie"
|
||||
@click="addCookie"
|
||||
class="q-mt-xs"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserCookieList",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [{ name: "", value: "" }],
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
methods: {
|
||||
addCookie() {
|
||||
const newValue = [...this.modelValue, { name: "", value: "" }];
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
removeCookie(index) {
|
||||
const newValue = [...this.modelValue];
|
||||
newValue.splice(index, 1);
|
||||
if (newValue.length === 0) {
|
||||
newValue.push({ name: "", value: "" });
|
||||
}
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
handleUpdate(index, field, value) {
|
||||
const newValue = [...this.modelValue];
|
||||
newValue[index] = { ...newValue[index], [field]: value };
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
:model-value="modelValue"
|
||||
:label="label"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="icon" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-select
|
||||
v-model="selectedDevice"
|
||||
:options="deviceOptions"
|
||||
label="常用设备"
|
||||
dense
|
||||
outlined
|
||||
emit-value
|
||||
map-options
|
||||
options-dense
|
||||
style="min-width: 150px"
|
||||
@update:model-value="handleDeviceSelect"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="list" />
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserDeviceName",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
return {
|
||||
selectedDevice: null,
|
||||
deviceOptions: [
|
||||
{ label: "iPhone 11", value: "iPhone 11" },
|
||||
{ label: "iPhone X", value: "iPhone X" },
|
||||
{ label: "iPad", value: "iPad" },
|
||||
{ label: "iPhone 6/7/8 Plus", value: "iPhone 6/7/8 Plus" },
|
||||
{ label: "iPhone 6/7/8", value: "iPhone 6/7/8" },
|
||||
{ label: "iPhone 5/SE", value: "iPhone 5/SE" },
|
||||
{ label: "HUAWEI Mate10", value: "HUAWEI Mate10" },
|
||||
{ label: "HUAWEI Mate20", value: "HUAWEI Mate20" },
|
||||
{ label: "HUAWEI Mate30", value: "HUAWEI Mate30" },
|
||||
{ label: "HUAWEI Mate30 Pro", value: "HUAWEI Mate30 Pro" },
|
||||
],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleDeviceSelect(value) {
|
||||
if (value) {
|
||||
this.$emit("update:modelValue", value);
|
||||
this.selectedDevice = null;
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
v-model.number="size.width"
|
||||
type="number"
|
||||
label="宽度"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="handleUpdate"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="width" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
v-model.number="size.height"
|
||||
type="number"
|
||||
label="高度"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="handleUpdate"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="height" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserDeviceSize",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
default: () => ({ width: 0, height: 0 }),
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
return {
|
||||
size: {
|
||||
width: this.modelValue.width,
|
||||
height: this.modelValue.height,
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleUpdate() {
|
||||
this.$emit("update:modelValue", { ...this.size });
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
deep: true,
|
||||
handler(newValue) {
|
||||
this.size = { ...newValue };
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div
|
||||
v-for="(file, index) in modelValue || []"
|
||||
:key="index"
|
||||
class="col-12"
|
||||
>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
:model-value="modelValue[index]"
|
||||
label="文件路径"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="(value) => handleUpdate(index, value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="negative"
|
||||
icon="remove"
|
||||
@click="removeFile(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
color="primary"
|
||||
icon="add"
|
||||
label="添加文件"
|
||||
@click="addFile"
|
||||
class="q-mt-xs"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserFileList",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
methods: {
|
||||
addFile() {
|
||||
const newValue = [...(this.modelValue || []), ""];
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
removeFile(index) {
|
||||
const newValue = [...this.modelValue];
|
||||
newValue.splice(index, 1);
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
handleUpdate(index, value) {
|
||||
const newValue = [...this.modelValue];
|
||||
newValue[index] = value;
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-3">
|
||||
<q-select
|
||||
v-model="localParams"
|
||||
use-input
|
||||
use-chips
|
||||
multiple
|
||||
dense
|
||||
hide-dropdown-icon
|
||||
options-dense
|
||||
input-debounce="0"
|
||||
new-value-mode="add-unique"
|
||||
label="参数"
|
||||
@update:model-value="updateParams"
|
||||
@input-value="handleInput"
|
||||
@blur="handleBlur"
|
||||
ref="paramSelect"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<div class="text-primary text-bold">(</div>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<div class="text-primary text-bold">)</div>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<q-input
|
||||
v-model="localFunction"
|
||||
:label="label"
|
||||
type="textarea"
|
||||
dense
|
||||
outlined
|
||||
autogrow
|
||||
@update:model-value="updateFunction"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<div class="text-primary text-bold">=> {</div>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<div class="text-primary text-bold">}</div>
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="localParams.length">
|
||||
<div v-for="param in localParams" :key="param" class="col-12">
|
||||
<div class="row q-col-gutter-sm items-center">
|
||||
<div class="col-3">
|
||||
<q-chip
|
||||
dense
|
||||
color="primary"
|
||||
text-color="white"
|
||||
removable
|
||||
@remove="removeParam(param)"
|
||||
>
|
||||
{{ param }}
|
||||
</q-chip>
|
||||
</div>
|
||||
<div class="col-9">
|
||||
<q-input
|
||||
v-model="paramValues[param]"
|
||||
:label="`传递给参数 ${param} 的值`"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateParamValue(param, $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserFunctionInput",
|
||||
props: {
|
||||
function: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
args: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: "函数内容",
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "code",
|
||||
},
|
||||
},
|
||||
emits: ["update:function", "update:args"],
|
||||
data() {
|
||||
return {
|
||||
localFunction: "",
|
||||
localParams: [],
|
||||
paramValues: {},
|
||||
newParamName: "",
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 初始化本地数据
|
||||
this.localFunction = this.function;
|
||||
this.localParams = this.args?.map((arg) => arg.name) || [];
|
||||
this.paramValues = Object.fromEntries(
|
||||
this.args?.map((arg) => [arg.name, arg.value]) || []
|
||||
);
|
||||
},
|
||||
methods: {
|
||||
updateFunction(value) {
|
||||
this.localFunction = value;
|
||||
this.emitUpdate();
|
||||
},
|
||||
updateParams(value) {
|
||||
this.localParams = value;
|
||||
this.emitUpdate();
|
||||
},
|
||||
removeParam(param) {
|
||||
const index = this.localParams.indexOf(param);
|
||||
if (index > -1) {
|
||||
this.localParams.splice(index, 1);
|
||||
delete this.paramValues[param];
|
||||
this.emitUpdate();
|
||||
}
|
||||
},
|
||||
updateParamValue(param, value) {
|
||||
this.paramValues[param] = value;
|
||||
this.emitUpdate();
|
||||
},
|
||||
emitUpdate() {
|
||||
this.$emit("update:function", this.localFunction);
|
||||
this.$emit(
|
||||
"update:args",
|
||||
this.localParams.map((name) => ({
|
||||
name,
|
||||
value: this.paramValues[name] || "",
|
||||
}))
|
||||
);
|
||||
},
|
||||
handleInput(val) {
|
||||
if (!val) return;
|
||||
this.newParamName = val;
|
||||
|
||||
if (val.includes(",") || val.includes(" ")) {
|
||||
const params = val
|
||||
.split(/[,\s]+/)
|
||||
.map((p) => p.trim())
|
||||
.filter((p) => p);
|
||||
params.forEach((param) => {
|
||||
if (param && !this.localParams.includes(param)) {
|
||||
this.localParams = [...this.localParams, param];
|
||||
this.paramValues[param] = "";
|
||||
}
|
||||
});
|
||||
this.newParamName = "";
|
||||
this.emitUpdate();
|
||||
this.$refs.paramSelect.updateInputValue("");
|
||||
}
|
||||
},
|
||||
handleBlur() {
|
||||
if (this.newParamName && !this.localParams.includes(this.newParamName)) {
|
||||
this.localParams = [...this.localParams, this.newParamName];
|
||||
this.paramValues[this.newParamName] = "";
|
||||
this.newParamName = "";
|
||||
this.emitUpdate();
|
||||
this.$refs.paramSelect.updateInputValue("");
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
function: {
|
||||
handler(newValue) {
|
||||
this.localFunction = newValue;
|
||||
},
|
||||
},
|
||||
args: {
|
||||
deep: true,
|
||||
handler(newValue) {
|
||||
this.localParams = newValue?.map((arg) => arg.name) || [];
|
||||
this.paramValues = Object.fromEntries(
|
||||
newValue?.map((arg) => [arg.name, arg.value]) || []
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,26 +1,16 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div :class="fullWidth ? 'col-12' : 'col-' + width">
|
||||
<q-input
|
||||
:value="modelValue"
|
||||
v-bind="$attrs"
|
||||
filled
|
||||
square
|
||||
:dense="!large"
|
||||
@update:modelValue="handleInput"
|
||||
>
|
||||
<template v-if="$slots.prepend" v-slot:prepend>
|
||||
<slot name="prepend" />
|
||||
</template>
|
||||
<template v-else-if="icon" v-slot:prepend>
|
||||
<q-icon :name="icon" />
|
||||
</template>
|
||||
<template v-if="$slots.append" v-slot:append>
|
||||
<slot name="append" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
</div>
|
||||
<q-input
|
||||
:model-value="modelValue"
|
||||
:label="label"
|
||||
:type="inputType"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="icon" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -28,38 +18,24 @@ import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserInput",
|
||||
inheritAttrs: false,
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: "",
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: 12,
|
||||
},
|
||||
fullWidth: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
flat: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
large: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
inputType: {
|
||||
type: String,
|
||||
default: "text",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
methods: {
|
||||
handleInput(value) {
|
||||
this.$emit("update:modelValue", value);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="text-caption q-mb-sm">{{ label }}</div>
|
||||
<div
|
||||
v-for="(param, index) in modelValue || []"
|
||||
:key="index"
|
||||
class="row q-col-gutter-sm q-mb-sm"
|
||||
>
|
||||
<div class="col-5">
|
||||
<q-input
|
||||
:model-value="param.name"
|
||||
label="参数名"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="(value) => handleUpdate(index, 'name', value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<q-input
|
||||
:model-value="param.value"
|
||||
label="传递给参数的值"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="(value) => handleUpdate(index, 'value', value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="negative"
|
||||
icon="remove"
|
||||
@click="removeParam(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
color="primary"
|
||||
icon="add"
|
||||
label="添加参数"
|
||||
@click="addParam"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserNamedParamList",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
methods: {
|
||||
addParam() {
|
||||
const newValue = [...(this.modelValue || []), { name: "", value: "" }];
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
removeParam(index) {
|
||||
const newValue = [...this.modelValue];
|
||||
newValue.splice(index, 1);
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
handleUpdate(index, field, value) {
|
||||
const newValue = [...this.modelValue];
|
||||
newValue[index] = { ...newValue[index], [field]: value };
|
||||
this.$emit("update:modelValue", newValue);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
@ -7,240 +7,119 @@
|
||||
>
|
||||
<!-- 复选框组 -->
|
||||
<template v-if="field.type === 'checkbox-group'">
|
||||
<div class="row items-center">
|
||||
<!-- <div class="text-caption q-mb-sm">{{ field.label }}</div> -->
|
||||
<q-option-group
|
||||
:model-value="fieldValue[field.key] || []"
|
||||
:options="field.options"
|
||||
type="checkbox"
|
||||
inline
|
||||
dense
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</div>
|
||||
<UBrowserCheckboxGroup
|
||||
v-model="fieldValue[field.key]"
|
||||
:options="field.options"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 单个复选框 -->
|
||||
<template v-else-if="field.type === 'checkbox'">
|
||||
<div class="row items-center no-wrap">
|
||||
<q-badge class="q-pa-xs">{{ field.label }}</q-badge>
|
||||
<q-btn-toggle
|
||||
:model-value="fieldValue[field.key] ? 'true' : 'false'"
|
||||
:options="[
|
||||
{ label: '是', value: 'true' },
|
||||
{ label: '否', value: 'false' },
|
||||
]"
|
||||
dense
|
||||
flat
|
||||
no-caps
|
||||
spread
|
||||
class="button-group"
|
||||
@update:model-value="updateValue(field.key, $event === 'true')"
|
||||
/>
|
||||
</div>
|
||||
<UBrowserCheckbox
|
||||
v-model="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 文本输入 -->
|
||||
<template v-else-if="field.type === 'input'">
|
||||
<q-input
|
||||
:model-value="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
:type="field.inputType || 'text'"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="field.icon" />
|
||||
</template>
|
||||
</q-input>
|
||||
<!-- 基本输入类型的处理 -->
|
||||
<template v-if="field.type === 'input'">
|
||||
<!-- 设备名称特殊处理 -->
|
||||
<template v-if="field.key === 'deviceName'">
|
||||
<UBrowserDeviceName
|
||||
v-model="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
:icon="field.icon"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
<!-- 普通输入框 -->
|
||||
<template v-else>
|
||||
<UBrowserInput
|
||||
v-model="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
:icon="field.icon"
|
||||
:input-type="field.inputType"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 文本区域 -->
|
||||
<template v-else-if="field.type === 'textarea'">
|
||||
<q-input
|
||||
:model-value="fieldValue[field.key]"
|
||||
<UBrowserTextarea
|
||||
v-model="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
type="textarea"
|
||||
dense
|
||||
outlined
|
||||
autogrow
|
||||
:icon="field.icon"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="field.icon" />
|
||||
</template>
|
||||
</q-input>
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 选择框 -->
|
||||
<template v-else-if="field.type === 'select'">
|
||||
<q-select
|
||||
:model-value="fieldValue[field.key]"
|
||||
<UBrowserSelect
|
||||
v-model="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
:icon="field.icon"
|
||||
:options="field.options"
|
||||
dense
|
||||
outlined
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="field.icon" />
|
||||
</template>
|
||||
</q-select>
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Cookie列表 -->
|
||||
<template v-else-if="field.type === 'cookie-list'">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div
|
||||
v-for="(cookie, index) in fieldValue[field.key] || [{}]"
|
||||
:key="index"
|
||||
class="col-12"
|
||||
>
|
||||
<div class="row items-center q-gutter-x-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="cookie.name"
|
||||
label="名称"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateCookieList(field.key)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="cookie.value"
|
||||
label="值"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateCookieList(field.key)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="negative"
|
||||
icon="remove"
|
||||
@click="removeCookie(field.key, index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
color="primary"
|
||||
icon="add"
|
||||
label="添加Cookie"
|
||||
@click="addCookie(field.key)"
|
||||
class="q-mt-xs"
|
||||
<UBrowserCookieList
|
||||
v-model="fieldValue[field.key]"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 参数列表 -->
|
||||
<template v-else-if="field.type === 'param-list'">
|
||||
<div class="text-caption q-mb-sm">{{ field.label }}</div>
|
||||
<div
|
||||
v-for="(param, index) in fieldValue[field.key] || []"
|
||||
:key="index"
|
||||
class="row q-col-gutter-sm q-mb-sm"
|
||||
>
|
||||
<div class="col-10">
|
||||
<q-input
|
||||
v-model="fieldValue[field.key][index]"
|
||||
label="参数值"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="
|
||||
updateValue(field.key, fieldValue[field.key])
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="negative"
|
||||
icon="remove"
|
||||
@click="removeParam(field.key, index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
color="primary"
|
||||
icon="add"
|
||||
label="添加参数"
|
||||
@click="addParam(field.key)"
|
||||
<!-- 命名参数列表 -->
|
||||
<template v-else-if="field.type === 'named-param-list'">
|
||||
<UBrowserNamedParamList
|
||||
v-model="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 文件列表 -->
|
||||
<template v-else-if="field.type === 'file-list'">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div
|
||||
v-for="(file, index) in fieldValue[field.key] || []"
|
||||
:key="index"
|
||||
class="col-12"
|
||||
>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
v-model="fieldValue[field.key][index]"
|
||||
label="文件路径"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="
|
||||
updateValue(field.key, fieldValue[field.key])
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="negative"
|
||||
icon="remove"
|
||||
@click="removeFile(field.key, index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
color="primary"
|
||||
icon="add"
|
||||
label="添加文件"
|
||||
@click="addFile(field.key)"
|
||||
class="q-mt-xs"
|
||||
<UBrowserFileList
|
||||
v-model="fieldValue[field.key]"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 按钮组 -->
|
||||
<template v-else-if="field.type === 'button-toggle'">
|
||||
<div class="row items-center no-wrap">
|
||||
<q-badge class="q-pa-xs">{{ field.label }}</q-badge>
|
||||
<q-btn-toggle
|
||||
:model-value="fieldValue[field.key]"
|
||||
:options="field.options"
|
||||
dense
|
||||
flat
|
||||
no-caps
|
||||
spread
|
||||
class="button-group"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</div>
|
||||
<UBrowserButtonToggle
|
||||
v-model="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
:options="field.options"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 设备尺寸 -->
|
||||
<template v-else-if="field.type === 'device-size'">
|
||||
<UBrowserDeviceSize
|
||||
v-model="fieldValue.size"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 带参数的函数输入 -->
|
||||
<template v-else-if="field.type === 'function-with-params'">
|
||||
<UBrowserFunctionInput
|
||||
v-model:function="fieldValue.function"
|
||||
v-model:args="fieldValue.args"
|
||||
:label="field.label"
|
||||
:icon="field.icon"
|
||||
@update:function="(value) => updateValue('function', value)"
|
||||
@update:args="(value) => updateValue('args', value)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@ -250,9 +129,35 @@
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import { get, set } from "lodash";
|
||||
import UBrowserFunctionInput from "./UBrowserFunctionInput.vue";
|
||||
import UBrowserCheckbox from "./UBrowserCheckbox.vue";
|
||||
import UBrowserFileList from "./UBrowserFileList.vue";
|
||||
import UBrowserCookieList from "./UBrowserCookieList.vue";
|
||||
import UBrowserButtonToggle from "./UBrowserButtonToggle.vue";
|
||||
import UBrowserDeviceSize from "./UBrowserDeviceSize.vue";
|
||||
import UBrowserNamedParamList from "./UBrowserNamedParamList.vue";
|
||||
import UBrowserSelect from "./UBrowserSelect.vue";
|
||||
import UBrowserDeviceName from "./UBrowserDeviceName.vue";
|
||||
import UBrowserTextarea from "./UBrowserTextarea.vue";
|
||||
import UBrowserInput from "./UBrowserInput.vue";
|
||||
import UBrowserCheckboxGroup from "./UBrowserCheckboxGroup.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserOperation",
|
||||
components: {
|
||||
UBrowserFunctionInput,
|
||||
UBrowserCheckbox,
|
||||
UBrowserFileList,
|
||||
UBrowserCookieList,
|
||||
UBrowserButtonToggle,
|
||||
UBrowserDeviceSize,
|
||||
UBrowserNamedParamList,
|
||||
UBrowserSelect,
|
||||
UBrowserDeviceName,
|
||||
UBrowserTextarea,
|
||||
UBrowserInput,
|
||||
UBrowserCheckboxGroup,
|
||||
},
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
@ -283,6 +188,11 @@ export default defineComponent({
|
||||
defaultValue = field.defaultValue || [];
|
||||
} else if (field.type === "checkbox") {
|
||||
defaultValue = field.defaultValue || false;
|
||||
} else if (field.type === "function-with-params") {
|
||||
// 为function-with-params类型设置特殊的默认值结构
|
||||
this.fieldValue.function = value?.function || "";
|
||||
this.fieldValue.args = value?.args || [];
|
||||
return; // 跳过后续的赋值
|
||||
} else {
|
||||
defaultValue = field.defaultValue;
|
||||
}
|
||||
@ -306,51 +216,6 @@ export default defineComponent({
|
||||
// 发出更新事件
|
||||
this.$emit("update:configs", newConfigs);
|
||||
},
|
||||
|
||||
// Cookie列表相关方法
|
||||
addCookie(key) {
|
||||
if (!this.fieldValue[key]) {
|
||||
this.fieldValue[key] = [];
|
||||
}
|
||||
this.fieldValue[key].push({ name: "", value: "" });
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
removeCookie(key, index) {
|
||||
this.fieldValue[key].splice(index, 1);
|
||||
if (this.fieldValue[key].length === 0) {
|
||||
this.fieldValue[key].push({ name: "", value: "" });
|
||||
}
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
updateCookieList(key) {
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
|
||||
// 参数列表相关方法
|
||||
addParam(key) {
|
||||
if (!this.fieldValue[key]) {
|
||||
this.fieldValue[key] = [];
|
||||
}
|
||||
this.fieldValue[key].push("");
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
removeParam(key, index) {
|
||||
this.fieldValue[key].splice(index, 1);
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
|
||||
// 文件列表相关方法
|
||||
addFile(key) {
|
||||
if (!this.fieldValue[key]) {
|
||||
this.fieldValue[key] = [];
|
||||
}
|
||||
this.fieldValue[key].push("");
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
removeFile(key, index) {
|
||||
this.fieldValue[key].splice(index, 1);
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
// 监听配置变化
|
||||
@ -359,6 +224,13 @@ export default defineComponent({
|
||||
handler() {
|
||||
this.fields.forEach((field) => {
|
||||
const value = get(this.configs[this.action], field.key);
|
||||
if (field.type === "function-with-params") {
|
||||
// 为function-with-params类型设置特殊的更新逻辑
|
||||
this.fieldValue.function =
|
||||
value?.function || this.fieldValue.function || "";
|
||||
this.fieldValue.args = value?.args || this.fieldValue.args || [];
|
||||
return;
|
||||
}
|
||||
if (value !== undefined) {
|
||||
this.fieldValue[field.key] = value;
|
||||
}
|
||||
@ -368,19 +240,3 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-group-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
flex: 1;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-btn) {
|
||||
min-height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<q-select
|
||||
:model-value="modelValue"
|
||||
:label="label"
|
||||
:options="options"
|
||||
dense
|
||||
outlined
|
||||
emit-value
|
||||
map-options
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="icon" />
|
||||
</template>
|
||||
</q-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserSelect",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: "",
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
});
|
||||
</script>
|
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<q-input
|
||||
:model-value="modelValue"
|
||||
:label="label"
|
||||
type="textarea"
|
||||
dense
|
||||
outlined
|
||||
autogrow
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="icon" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserTextarea",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
});
|
||||
</script>
|
629
src/components/editor/composer/ubrowser/ubrowserConfig.js
Normal file
629
src/components/editor/composer/ubrowser/ubrowserConfig.js
Normal file
@ -0,0 +1,629 @@
|
||||
// ubrowser 浏览器操作配置
|
||||
export const ubrowserOperationConfigs = {
|
||||
wait: {
|
||||
label: "等待",
|
||||
config: [
|
||||
{
|
||||
key: "type",
|
||||
label: "等待类型",
|
||||
type: "button-toggle",
|
||||
options: [
|
||||
{ label: "等待时间", value: "time" },
|
||||
{ label: "等待元素", value: "selector" },
|
||||
{ label: "等待条件", value: "function" },
|
||||
],
|
||||
defaultValue: "time",
|
||||
},
|
||||
{
|
||||
key: "time",
|
||||
label: "等待时间(ms)",
|
||||
icon: "timer",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "time",
|
||||
},
|
||||
{
|
||||
key: "selector",
|
||||
label: "等待元素的CSS选择器",
|
||||
icon: "find_in_page",
|
||||
type: "input",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "selector",
|
||||
},
|
||||
{
|
||||
key: "function",
|
||||
label: "等待条件(返回 true 时结束等待)",
|
||||
icon: "code",
|
||||
type: "function-with-params",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "function",
|
||||
},
|
||||
{
|
||||
key: "timeout",
|
||||
label: "超时时间(ms)",
|
||||
icon: "timer_off",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 12,
|
||||
defaultValue: 60000,
|
||||
showWhen: "type",
|
||||
showValue: ["selector", "function"],
|
||||
},
|
||||
],
|
||||
icon: "timer",
|
||||
},
|
||||
click: {
|
||||
label: "点击",
|
||||
config: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "点击元素的CSS选择器",
|
||||
icon: "mouse",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
icon: "mouse",
|
||||
},
|
||||
css: {
|
||||
label: "注入CSS",
|
||||
config: [
|
||||
{
|
||||
key: "value",
|
||||
label: "注入的CSS样式",
|
||||
icon: "style",
|
||||
type: "textarea",
|
||||
},
|
||||
],
|
||||
icon: "style",
|
||||
},
|
||||
press: {
|
||||
label: "按键",
|
||||
config: [
|
||||
{
|
||||
key: "key",
|
||||
label: "按键",
|
||||
icon: "keyboard",
|
||||
type: "input",
|
||||
width: 5,
|
||||
},
|
||||
{
|
||||
key: "modifiers",
|
||||
label: "修饰键",
|
||||
type: "checkbox-group",
|
||||
options: [
|
||||
{ label: "Ctrl", value: "ctrl" },
|
||||
{ label: "Shift", value: "shift" },
|
||||
{ label: "Alt", value: "alt" },
|
||||
{ label: "Meta", value: "meta" },
|
||||
],
|
||||
defaultValue: [],
|
||||
width: 7,
|
||||
},
|
||||
],
|
||||
icon: "keyboard",
|
||||
},
|
||||
paste: {
|
||||
label: "粘贴",
|
||||
config: [
|
||||
{
|
||||
key: "text",
|
||||
label: "粘贴内容",
|
||||
icon: "content_paste",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
icon: "content_paste",
|
||||
},
|
||||
viewport: {
|
||||
label: "视窗",
|
||||
config: [
|
||||
{
|
||||
key: "width",
|
||||
label: "视窗宽度",
|
||||
icon: "width",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "height",
|
||||
label: "视窗高度",
|
||||
icon: "height",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
},
|
||||
],
|
||||
icon: "crop",
|
||||
},
|
||||
screenshot: {
|
||||
label: "截图",
|
||||
config: [
|
||||
{ key: "selector", label: "元素选择器", icon: "crop", type: "input" },
|
||||
{
|
||||
key: "rect.x",
|
||||
label: "X坐标",
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.y",
|
||||
label: "Y坐标",
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.width",
|
||||
label: "宽度",
|
||||
icon: "width",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.height",
|
||||
label: "高度",
|
||||
icon: "height",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 3,
|
||||
},
|
||||
{ key: "savePath", label: "保存路径", icon: "save", type: "input" },
|
||||
],
|
||||
icon: "picture_as_pdf",
|
||||
},
|
||||
pdf: {
|
||||
label: "导出PDF",
|
||||
config: [
|
||||
{
|
||||
key: "options.marginsType",
|
||||
label: "边距类型",
|
||||
type: "select",
|
||||
options: [
|
||||
{ label: "默认边距", value: 0 },
|
||||
{ label: "无边距", value: 1 },
|
||||
{ label: "最小边距", value: 2 },
|
||||
],
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "options.pageSize",
|
||||
label: "页面大小",
|
||||
type: "select",
|
||||
options: ["A3", "A4", "A5", "Legal", "Letter", "Tabloid"],
|
||||
width: 6,
|
||||
},
|
||||
{ key: "savePath", label: "保存路径", icon: "save", type: "input" },
|
||||
],
|
||||
icon: "devices",
|
||||
},
|
||||
device: {
|
||||
label: "模拟设备",
|
||||
config: [
|
||||
{
|
||||
key: "type",
|
||||
label: "设备类型",
|
||||
type: "button-toggle",
|
||||
options: [
|
||||
{ label: "特定设备", value: "preset" },
|
||||
{ label: "自定义设备", value: "custom" },
|
||||
],
|
||||
defaultValue: "preset",
|
||||
},
|
||||
{
|
||||
key: "deviceName",
|
||||
label: "设备名称",
|
||||
icon: "smartphone",
|
||||
type: "input",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "preset",
|
||||
},
|
||||
{
|
||||
key: "size",
|
||||
label: "设备尺寸",
|
||||
type: "device-size",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "custom",
|
||||
},
|
||||
{
|
||||
key: "useragent",
|
||||
label: "User-Agent",
|
||||
icon: "devices",
|
||||
type: "input",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "custom",
|
||||
},
|
||||
],
|
||||
icon: "cookie",
|
||||
},
|
||||
setCookies: {
|
||||
label: "设置Cookie",
|
||||
config: [{ key: "items", label: "Cookie列表", type: "cookie-list" }],
|
||||
icon: "cookie",
|
||||
},
|
||||
removeCookies: {
|
||||
label: "删除Cookie",
|
||||
config: [
|
||||
{ key: "name", label: "Cookie名称", icon: "cookie", type: "input" },
|
||||
],
|
||||
icon: "cookie",
|
||||
},
|
||||
clearCookies: {
|
||||
label: "清空Cookie",
|
||||
config: [{ key: "url", label: "URL(可选)", icon: "link", type: "input" }],
|
||||
icon: "cookie",
|
||||
},
|
||||
evaluate: {
|
||||
label: "执行代码",
|
||||
config: [
|
||||
{
|
||||
key: "function",
|
||||
label: "执行的代码",
|
||||
icon: "code",
|
||||
type: "function-with-params",
|
||||
width: 12,
|
||||
},
|
||||
],
|
||||
icon: "code",
|
||||
},
|
||||
when: {
|
||||
label: "条件判断",
|
||||
config: [
|
||||
{
|
||||
key: "type",
|
||||
label: "条件类型",
|
||||
type: "button-toggle",
|
||||
options: [
|
||||
{ label: "等待元素", value: "selector" },
|
||||
{ label: "等待条件", value: "function" },
|
||||
],
|
||||
defaultValue: "selector",
|
||||
},
|
||||
{
|
||||
key: "selector",
|
||||
label: "等待元素的CSS选择器",
|
||||
icon: "find_in_page",
|
||||
type: "input",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "selector",
|
||||
},
|
||||
{
|
||||
key: "function",
|
||||
label: "等待条件(返回 true 时结束等待)",
|
||||
icon: "code",
|
||||
type: "function-with-params",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "function",
|
||||
},
|
||||
{
|
||||
key: "timeout",
|
||||
label: "超时时间(ms)",
|
||||
icon: "timer_off",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 12,
|
||||
defaultValue: 60000,
|
||||
showWhen: "type",
|
||||
showValue: ["selector", "function"],
|
||||
},
|
||||
],
|
||||
icon: "rule",
|
||||
},
|
||||
end: {
|
||||
label: "结束条件",
|
||||
config: [],
|
||||
icon: "stop",
|
||||
},
|
||||
mousedown: {
|
||||
label: "按下鼠标",
|
||||
config: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "按下元素选择器",
|
||||
icon: "mouse",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
icon: "mouse",
|
||||
},
|
||||
mouseup: {
|
||||
label: "释放鼠标",
|
||||
config: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "释放元素选择器",
|
||||
icon: "mouse",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
icon: "mouse",
|
||||
},
|
||||
file: {
|
||||
label: "上传文件",
|
||||
config: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "文件输入框选择器",
|
||||
icon: "upload_file",
|
||||
type: "input",
|
||||
},
|
||||
{ key: "files", label: "文件列表", type: "file-list", width: 12 },
|
||||
],
|
||||
icon: "upload_file",
|
||||
},
|
||||
value: {
|
||||
label: "设置值",
|
||||
config: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "元素选择器",
|
||||
icon: "input",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "value",
|
||||
label: "设置的值",
|
||||
icon: "edit",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
],
|
||||
icon: "check_box",
|
||||
},
|
||||
check: {
|
||||
label: "设置选中",
|
||||
config: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "复选框/选框选择器",
|
||||
icon: "check_box",
|
||||
type: "input",
|
||||
width: 8,
|
||||
},
|
||||
{
|
||||
key: "checked",
|
||||
label: "选中状态",
|
||||
type: "checkbox",
|
||||
defaultValue: false,
|
||||
width: 4,
|
||||
},
|
||||
],
|
||||
icon: "center_focus_strong",
|
||||
},
|
||||
focus: {
|
||||
label: "聚焦元素",
|
||||
config: [
|
||||
{
|
||||
key: "selector",
|
||||
label: "元素选择器",
|
||||
icon: "center_focus_strong",
|
||||
type: "input",
|
||||
},
|
||||
],
|
||||
icon: "swap_vert",
|
||||
},
|
||||
scroll: {
|
||||
label: "滚动",
|
||||
config: [
|
||||
{
|
||||
key: "type",
|
||||
label: "滚动类型",
|
||||
type: "button-toggle",
|
||||
options: [
|
||||
{ label: "滚动到元素", value: "element" },
|
||||
{ label: "滚动到坐标", value: "position" },
|
||||
],
|
||||
defaultValue: "element",
|
||||
},
|
||||
{
|
||||
key: "selector",
|
||||
label: "目标元素选择器",
|
||||
icon: "swap_vert",
|
||||
type: "input",
|
||||
width: 12,
|
||||
showWhen: "type",
|
||||
showValue: "element",
|
||||
},
|
||||
{
|
||||
key: "x",
|
||||
label: "X坐标",
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
showWhen: "type",
|
||||
showValue: "position",
|
||||
},
|
||||
{
|
||||
key: "y",
|
||||
label: "Y坐标",
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
showWhen: "type",
|
||||
showValue: "position",
|
||||
},
|
||||
],
|
||||
icon: "download",
|
||||
},
|
||||
download: {
|
||||
label: "下载",
|
||||
config: [
|
||||
{
|
||||
key: "url",
|
||||
label: "下载URL",
|
||||
icon: "link",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "savePath",
|
||||
label: "保存路径",
|
||||
icon: "save",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
],
|
||||
icon: "download",
|
||||
},
|
||||
devTools: {
|
||||
label: "开发工具",
|
||||
config: [
|
||||
{
|
||||
key: "mode",
|
||||
label: "开发工具位置",
|
||||
type: "button-toggle",
|
||||
options: [
|
||||
{ label: "右侧", value: "right" },
|
||||
{ label: "底部", value: "bottom" },
|
||||
{ label: "独立", value: "undocked" },
|
||||
{ label: "分离", value: "detach" },
|
||||
],
|
||||
defaultValue: "right",
|
||||
},
|
||||
],
|
||||
icon: "developer_board",
|
||||
},
|
||||
};
|
||||
|
||||
// 添加默认运行配置
|
||||
const defaultUBrowserRunConfigs = {
|
||||
show: true,
|
||||
width: 1280,
|
||||
height: 800,
|
||||
center: true,
|
||||
minWidth: 800,
|
||||
minHeight: 600,
|
||||
resizable: true,
|
||||
movable: true,
|
||||
minimizable: true,
|
||||
maximizable: true,
|
||||
alwaysOnTop: false,
|
||||
fullscreen: false,
|
||||
fullscreenable: true,
|
||||
enableLargerThanScreen: false,
|
||||
opacity: 1,
|
||||
};
|
||||
|
||||
// ubrowser 默认配置 基础参数-浏览器操作-运行参数
|
||||
export const defaultUBrowserConfigs = {
|
||||
// 基础参数
|
||||
useragent: {
|
||||
value: "",
|
||||
},
|
||||
goto: {
|
||||
url: "",
|
||||
headers: {
|
||||
Referer: "",
|
||||
userAgent: "",
|
||||
},
|
||||
timeout: 60000,
|
||||
},
|
||||
// 浏览器操作
|
||||
wait: {
|
||||
value: "",
|
||||
timeout: 60000,
|
||||
},
|
||||
click: {
|
||||
selector: "",
|
||||
},
|
||||
css: {
|
||||
value: "",
|
||||
},
|
||||
press: {
|
||||
key: "",
|
||||
modifiers: [],
|
||||
},
|
||||
paste: {
|
||||
text: "",
|
||||
},
|
||||
screenshot: {
|
||||
selector: "",
|
||||
rect: { x: 0, y: 0, width: 0, height: 0 },
|
||||
savePath: "",
|
||||
},
|
||||
pdf: {
|
||||
options: {
|
||||
marginsType: 0,
|
||||
pageSize: "A4",
|
||||
},
|
||||
savePath: "",
|
||||
},
|
||||
device: {
|
||||
size: { width: 1280, height: 800 },
|
||||
useragent: "",
|
||||
},
|
||||
cookies: {
|
||||
name: "",
|
||||
},
|
||||
setCookies: {
|
||||
items: [{ name: "", value: "" }],
|
||||
},
|
||||
removeCookies: {
|
||||
name: "",
|
||||
},
|
||||
clearCookies: {
|
||||
url: "",
|
||||
},
|
||||
evaluate: {
|
||||
function: "",
|
||||
params: [],
|
||||
},
|
||||
when: {
|
||||
condition: "",
|
||||
},
|
||||
mousedown: {
|
||||
selector: "",
|
||||
},
|
||||
mouseup: {
|
||||
selector: "",
|
||||
},
|
||||
file: {
|
||||
selector: "",
|
||||
files: [],
|
||||
},
|
||||
value: {
|
||||
selector: "",
|
||||
value: "",
|
||||
},
|
||||
check: {
|
||||
selector: "",
|
||||
checked: false,
|
||||
},
|
||||
focus: {
|
||||
selector: "",
|
||||
},
|
||||
scroll: {
|
||||
target: "",
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
download: {
|
||||
url: "",
|
||||
savePath: "",
|
||||
},
|
||||
// 运行参数
|
||||
run: defaultUBrowserRunConfigs,
|
||||
};
|
||||
|
||||
// 定义 ubrowser 操作图标映射
|
||||
// 格式:{ wait: 'timer', ...}
|
||||
export const ubrowserActionIcons = _.mapValues(
|
||||
ubrowserOperationConfigs,
|
||||
(config) => config.icon
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user