重构ubrowser

This commit is contained in:
fofolee 2025-01-05 09:51:44 +08:00
parent 827c702e50
commit e082304c56
18 changed files with 857 additions and 843 deletions

View File

@ -33,7 +33,8 @@
</div> </div>
<div class="col-auto"> <div class="col-auto">
<q-select <q-select
v-model="selectedUA" :model-value="selectedUA"
@update:model-value="handleUAChange"
:options="userAgentOptions" :options="userAgentOptions"
label="常用 UA" label="常用 UA"
dense dense
@ -66,11 +67,11 @@
</template> </template>
<script> <script>
import { defineComponent } from "vue"; import { defineComponent, ref, computed } from "vue";
import { defaultUBrowserConfigs } from "js/composer/ubrowserConfig";
import { userAgent } from "js/options/httpOptions"; import { userAgent } from "js/options/httpOptions";
import VariableInput from "components/composer/ui/VariableInput.vue"; import VariableInput from "components/composer/ui/VariableInput.vue";
import NumberInput from "components/composer/ui/NumberInput.vue"; import NumberInput from "components/composer/ui/NumberInput.vue";
export default defineComponent({ export default defineComponent({
name: "UBrowserBasic", name: "UBrowserBasic",
components: { components: {
@ -83,37 +84,47 @@ export default defineComponent({
required: true, required: true,
}, },
}, },
data() { emits: ["update:configs"],
return { setup(props, { emit }) {
selectedUA: null, const selectedUA = ref(null);
localConfigs: defaultUBrowserConfigs.goto,
userAgentOptions: userAgent, // 使 computed
const localConfigs = computed({
get: () => props.configs,
set: (val) => {
emit("update:configs", val);
},
});
//
const updateConfigs = () => {
emit("update:configs", localConfigs.value);
}; };
},
created() { // UA
// const handleUAChange = (val) => {
this.localConfigs = window.lodashM.cloneDeep(this.configs); if (!val) return;
},
methods: { const newConfigs = window.lodashM.cloneDeep(props.configs);
updateConfigs() { if (!newConfigs.goto.headers) {
this.$emit("update:configs", window.lodashM.cloneDeep(this.localConfigs)); newConfigs.goto.headers = {};
},
},
watch: {
configs: {
deep: true,
handler(newConfigs) {
this.localConfigs = window.lodashM.cloneDeep(newConfigs);
},
},
selectedUA(value) {
if (value) {
this.localConfigs.goto.headers.userAgent.value = value;
this.localConfigs.goto.headers.userAgent.isString = true;
this.updateConfigs();
this.selectedUA = null;
} }
}, newConfigs.goto.headers.userAgent = {
value: val,
isString: true,
__varInputVal__: true,
};
emit("update:configs", newConfigs);
selectedUA.value = null;
};
return {
selectedUA,
localConfigs,
userAgentOptions: userAgent,
updateConfigs,
handleUAChange,
};
}, },
}); });
</script> </script>

View File

@ -15,7 +15,7 @@
</q-tabs> </q-tabs>
<!-- 内容区域 --> <!-- 内容区域 -->
<q-tab-panels v-model="step" animated swipeable class="ubrowser-panels"> <q-tab-panels v-model="step" class="ubrowser-panels">
<q-tab-panel name="1" class="panel-content"> <q-tab-panel name="1" class="panel-content">
<UBrowserBasic :configs="configs" @update:configs="updateConfigs" /> <UBrowserBasic :configs="configs" @update:configs="updateConfigs" />
</q-tab-panel> </q-tab-panel>
@ -23,8 +23,9 @@
<q-tab-panel name="2" class="panel-content"> <q-tab-panel name="2" class="panel-content">
<UBrowserOperations <UBrowserOperations
:configs="configs" :configs="configs"
:selected-actions="selectedActions"
@update:configs="updateConfigs" @update:configs="updateConfigs"
v-model:selected-actions="selectedActions" @update:selected-actions="(val) => (selectedActions = val)"
@remove-action="removeAction" @remove-action="removeAction"
/> />
</q-tab-panel> </q-tab-panel>
@ -37,7 +38,7 @@
</template> </template>
<script> <script>
import { defineComponent } from "vue"; import { defineComponent, ref, computed } from "vue";
import UBrowserBasic from "./UBrowserBasic.vue"; import UBrowserBasic from "./UBrowserBasic.vue";
import UBrowserOperations from "./UBrowserOperations.vue"; import UBrowserOperations from "./UBrowserOperations.vue";
import UBrowserRun from "./UBrowserRun.vue"; import UBrowserRun from "./UBrowserRun.vue";
@ -53,117 +54,115 @@ export default defineComponent({
}, },
props: { props: {
modelValue: { modelValue: {
type: String, type: Object,
default: "", required: true,
}, },
}, },
emits: ["update:modelValue"], emits: ["update:modelValue"],
data() { setup(props, { emit }) {
return { //
step: "1", const step = ref("1");
selectedActions: [], const selectedActions = ref([]);
configs: window.lodashM.cloneDeep(defaultUBrowserConfigs),
// run
const localConfigs = ref(window.lodashM.cloneDeep(defaultUBrowserConfigs));
if (props.modelValue?.argvs) {
// run
localConfigs.value = {
...localConfigs.value,
...props.modelValue.argvs,
run: {
...localConfigs.value.run,
...props.modelValue.argvs.run,
},
}; };
}
// argvs
const argvs = computed({
get: () => localConfigs.value,
set: (val) => {
// run
const newConfigs = {
...val,
run: {
...localConfigs.value.run,
...val.run,
}, },
methods: { };
updateConfigs(newConfigs) { localConfigs.value = newConfigs;
this.configs = newConfigs; emit("update:modelValue", {
...props.modelValue,
argvs: newConfigs,
code: generateUBrowserCode(newConfigs, selectedActions.value),
});
}, },
removeAction(action) { });
const newActions = this.selectedActions.filter((a) => a.id !== action.id);
this.selectedActions = newActions; //
const newConfigs = { ...this.configs }; const updateConfigs = (newConfigs) => {
argvs.value = window.lodashM.cloneDeep(newConfigs);
};
//
const removeAction = (action) => {
selectedActions.value = selectedActions.value.filter(
(a) => a.id !== action.id
);
const newConfigs = { ...argvs.value };
delete newConfigs[action.value]; delete newConfigs[action.value];
this.configs = newConfigs; argvs.value = newConfigs;
}, };
},
watch: { return {
configs: { step,
deep: true, selectedActions,
handler() { configs: argvs,
this.$emit( updateConfigs,
"update:modelValue", removeAction,
generateUBrowserCode(this.configs, this.selectedActions) };
);
},
},
selectedActions: {
handler() {
this.$emit(
"update:modelValue",
generateUBrowserCode(this.configs, this.selectedActions)
);
},
},
step: {
handler() {
this.$emit(
"update:modelValue",
generateUBrowserCode(this.configs, this.selectedActions)
);
},
},
}, },
}); });
</script> </script>
<style scoped> <style>
.ubrowser-editor { .ubrowser-editor {
width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
min-height: 0; width: 100%;
} }
.ubrowser-tabs { .ubrowser-tabs {
background: rgba(255, 255, 255, 0.8);
border-radius: 4px 4px 0 0;
flex-shrink: 0; flex-shrink: 0;
} }
.ubrowser-panels { .ubrowser-panels {
background: rgba(255, 255, 255, 0.8);
border-radius: 0 0 4px 4px;
flex: 1; flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
/* 调整面板内边距和布局 */
.ubrowser-panels :deep(.q-tab-panel) {
padding: 8px;
height: 100%;
min-height: 0;
}
/* 面板内容区域 */
.panel-content {
height: 100%;
overflow: auto; overflow: auto;
} }
/* 调整标签页样式 */ .panel-content {
padding: 16px;
min-height: 200px;
}
.ubrowser-panels :deep(.q-tab-panel) {
padding: 0;
}
.ubrowser-tabs :deep(.q-tab) { .ubrowser-tabs :deep(.q-tab) {
min-height: 36px; min-height: 40px;
padding: 0 12px; padding: 0 16px;
}
.ubrowser-tabs :deep(.q-tab__content) {
min-width: 0;
flex-direction: row;
gap: 4px;
}
.ubrowser-tabs :deep(.q-tab__label) {
font-size: 12px;
line-height: 1;
} }
.ubrowser-tabs :deep(.q-tab__icon) { .ubrowser-tabs :deep(.q-tab__icon) {
font-size: 16px; font-size: 20px;
margin: 0; }
.ubrowser-tabs :deep(.q-tab__label) {
font-size: 14px;
line-height: 1.2;
margin-left: 8px;
} }
</style> </style>

View File

@ -106,20 +106,21 @@ export default defineComponent({
required: true, required: true,
}, },
}, },
data() { emits: ["remove-action", "update:selectedActions", "update:configs"],
setup() {
return { return {
ubrowserOperationConfigs: ubrowserOperationConfigs, ubrowserOperationConfigs,
}; };
}, },
emits: ["remove-action", "update:selectedActions", "update:configs"],
methods: { methods: {
moveAction(index, direction) { moveAction(index, direction) {
const newIndex = index + direction; const newIndex = index + direction;
if (newIndex >= 0 && newIndex < this.selectedActions.length) { if (newIndex >= 0 && newIndex < this.selectedActions.length) {
const actions = [...this.selectedActions]; const actions = [...this.selectedActions];
const temp = actions[index]; [actions[index], actions[newIndex]] = [
actions[index] = actions[newIndex]; actions[newIndex],
actions[newIndex] = temp; actions[index],
];
this.$emit("update:selectedActions", actions); this.$emit("update:selectedActions", actions);
} }
}, },
@ -127,11 +128,10 @@ export default defineComponent({
const index = this.selectedActions.findIndex( const index = this.selectedActions.findIndex(
(a) => a.value === action.value (a) => a.value === action.value
); );
if (index === -1) { if (index === -1) {
// //
this.$emit("update:selectedActions", [ const newAction = {
...this.selectedActions,
{
...action, ...action,
id: this.$root.getUniqueId(), id: this.$root.getUniqueId(),
argv: "", argv: "",
@ -139,7 +139,11 @@ export default defineComponent({
useOutput: null, useOutput: null,
cmd: action.value || action.cmd, cmd: action.value || action.cmd,
value: action.value || action.cmd, value: action.value || action.cmd,
}, };
this.$emit("update:selectedActions", [
...this.selectedActions,
newAction,
]); ]);
// //
@ -148,7 +152,6 @@ export default defineComponent({
const newConfigs = { ...this.configs }; const newConfigs = { ...this.configs };
if (!newConfigs[action.value]) { if (!newConfigs[action.value]) {
newConfigs[action.value] = {}; newConfigs[action.value] = {};
}
// //
config.forEach((field) => { config.forEach((field) => {
if (field.defaultValue !== undefined) { if (field.defaultValue !== undefined) {
@ -157,6 +160,7 @@ export default defineComponent({
}); });
this.$emit("update:configs", newConfigs); this.$emit("update:configs", newConfigs);
} }
}
} else { } else {
// //
const newActions = [...this.selectedActions]; const newActions = [...this.selectedActions];
@ -167,7 +171,7 @@ export default defineComponent({
getActionProps(action, key) { getActionProps(action, key) {
return this.ubrowserOperationConfigs.find( return this.ubrowserOperationConfigs.find(
(a) => a.value === action.value (a) => a.value === action.value
)[key]; )?.[key];
}, },
}, },
}); });

View File

@ -155,7 +155,7 @@
</template> </template>
<script> <script>
import { defineComponent } from "vue"; import { defineComponent, ref, computed } from "vue";
import NumberInput from "components/composer/ui/NumberInput.vue"; import NumberInput from "components/composer/ui/NumberInput.vue";
export default defineComponent({ export default defineComponent({
@ -170,24 +170,24 @@ export default defineComponent({
}, },
}, },
emits: ["update:configs"], emits: ["update:configs"],
data() { setup(props, { emit }) {
return { const localConfigs = ref(window.lodashM.cloneDeep(props.configs));
localConfigs: window.lodashM.cloneDeep(this.configs),
// configs
const watchConfigs = computed(() => props.configs);
watchConfigs.value &&
(localConfigs.value = window.lodashM.cloneDeep(props.configs));
//
const updateConfig = (key, value) => {
localConfigs.value.run[key] = value;
emit("update:configs", window.lodashM.cloneDeep(localConfigs.value));
};
return {
localConfigs,
updateConfig,
}; };
},
methods: {
updateConfig(key, value) {
this.localConfigs.run[key] = value;
this.$emit("update:configs", window.lodashM.cloneDeep(this.localConfigs));
},
},
watch: {
configs: {
deep: true,
handler(newConfigs) {
this.localConfigs = window.lodashM.cloneDeep(newConfigs);
},
},
}, },
}); });
</script> </script>

View File

@ -1,50 +0,0 @@
<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>

View File

@ -1,37 +0,0 @@
<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>

View File

@ -1,31 +0,0 @@
<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>

View File

@ -11,7 +11,7 @@
<VariableInput <VariableInput
:model-value="cookie.name" :model-value="cookie.name"
label="名称" label="名称"
:command="{ icon: 'label' }" icon="label"
@update:model-value=" @update:model-value="
(value) => handleUpdate(index, 'name', value) (value) => handleUpdate(index, 'name', value)
" "
@ -21,7 +21,7 @@
<VariableInput <VariableInput
:model-value="cookie.value" :model-value="cookie.value"
label="值" label="值"
:command="{ icon: 'edit' }" icon="edit"
@update:model-value=" @update:model-value="
(value) => handleUpdate(index, 'value', value) (value) => handleUpdate(index, 'value', value)
" "
@ -64,20 +64,58 @@ export default defineComponent({
props: { props: {
modelValue: { modelValue: {
type: Array, type: Array,
default: () => [{ name: "", value: "" }], default: () => [
{
name: {
value: "",
isString: true,
__varInputVal__: true,
},
value: {
value: "",
isString: true,
__varInputVal__: true,
},
},
],
}, },
}, },
emits: ["update:modelValue"], emits: ["update:modelValue"],
methods: { methods: {
addCookie() { addCookie() {
const newValue = [...this.modelValue, { name: "", value: "" }]; const newValue = [
...this.modelValue,
{
name: {
value: "",
isString: true,
__varInputVal__: true,
},
value: {
value: "",
isString: true,
__varInputVal__: true,
},
},
];
this.$emit("update:modelValue", newValue); this.$emit("update:modelValue", newValue);
}, },
removeCookie(index) { removeCookie(index) {
const newValue = [...this.modelValue]; const newValue = [...this.modelValue];
newValue.splice(index, 1); newValue.splice(index, 1);
if (newValue.length === 0) { if (newValue.length === 0) {
newValue.push({ name: "", value: "" }); newValue.push({
name: {
value: "",
isString: true,
__varInputVal__: true,
},
value: {
value: "",
isString: true,
__varInputVal__: true,
},
});
} }
this.$emit("update:modelValue", newValue); this.$emit("update:modelValue", newValue);
}, },

View File

@ -2,7 +2,7 @@
<div class="row q-col-gutter-sm"> <div class="row q-col-gutter-sm">
<div class="col"> <div class="col">
<VariableInput <VariableInput
:command="{ icon: icon }" icon="label"
:model-value="modelValue" :model-value="modelValue"
:label="label" :label="label"
@update:model-value="$emit('update:modelValue', $event)" @update:model-value="$emit('update:modelValue', $event)"
@ -41,8 +41,12 @@ export default defineComponent({
}, },
props: { props: {
modelValue: { modelValue: {
type: String, type: Object,
default: "", default: () => ({
value: "",
isString: true,
__varInputVal__: true,
}),
}, },
label: { label: {
type: String, type: String,

View File

@ -1,60 +0,0 @@
<template>
<div class="row q-col-gutter-sm">
<div class="col-6">
<VariableInput
v-model.number="size.width"
label="宽度"
:command="{ icon: 'width', inputType: 'number' }"
@update:model-value="handleUpdate"
/>
</div>
<div class="col-6">
<VariableInput
v-model.number="size.height"
label="高度"
:command="{ icon: 'height', inputType: 'number' }"
@update:model-value="handleUpdate"
/>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import VariableInput from "components/composer/ui/VariableInput.vue";
export default defineComponent({
name: "UBrowserDeviceSize",
components: {
VariableInput,
},
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>

View File

@ -58,7 +58,14 @@ export default defineComponent({
emits: ["update:modelValue"], emits: ["update:modelValue"],
methods: { methods: {
addFile() { addFile() {
const newValue = [...(this.modelValue || []), ""]; const newValue = [
...(this.modelValue || []),
{
value: "",
isString: true,
__varInputVal__: true,
},
];
this.$emit("update:modelValue", newValue); this.$emit("update:modelValue", newValue);
}, },
removeFile(index) { removeFile(index) {

View File

@ -10,7 +10,7 @@
<VariableInput <VariableInput
:model-value="param.name" :model-value="param.name"
label="参数名" label="参数名"
:command="{ icon: 'label' }" icon="label"
@update:model-value="(value) => handleUpdate(index, 'name', value)" @update:model-value="(value) => handleUpdate(index, 'name', value)"
/> />
</div> </div>
@ -18,7 +18,7 @@
<VariableInput <VariableInput
:model-value="param.value" :model-value="param.value"
label="传递给参数的值" label="传递给参数的值"
:command="{ icon: 'edit' }" icon="edit"
@update:model-value="(value) => handleUpdate(index, 'value', value)" @update:model-value="(value) => handleUpdate(index, 'value', value)"
/> />
</div> </div>
@ -56,14 +56,41 @@ export default defineComponent({
props: { props: {
modelValue: { modelValue: {
type: Array, type: Array,
default: () => [{ name: "", value: "" }], default: () => [
{
name: {
value: "",
isString: true,
__varInputVal__: true,
},
value: {
value: "",
isString: true,
__varInputVal__: true,
},
},
],
}, },
label: String, label: String,
}, },
emits: ["update:modelValue"], emits: ["update:modelValue"],
methods: { methods: {
addParam() { addParam() {
const newValue = [...(this.modelValue || []), { name: "", value: "" }]; const newValue = [
...(this.modelValue || []),
{
name: {
value: "",
isString: true,
__varInputVal__: true,
},
value: {
value: "",
isString: true,
__varInputVal__: true,
},
},
];
this.$emit("update:modelValue", newValue); this.$emit("update:modelValue", newValue);
}, },
removeParam(index) { removeParam(index) {

View File

@ -7,20 +7,37 @@
> >
<!-- 复选框组 --> <!-- 复选框组 -->
<template v-if="field.type === 'checkbox-group'"> <template v-if="field.type === 'checkbox-group'">
<UBrowserCheckboxGroup <q-option-group
v-model="fieldValue[field.key]" :model-value="
Array.isArray(fieldValue[field.key]) ? fieldValue[field.key] : []
"
:options="field.options" :options="field.options"
type="checkbox"
class="row items-center"
inline
dense
@update:model-value="updateValue(field.key, $event)" @update:model-value="updateValue(field.key, $event)"
/> />
</template> </template>
<!-- 单个复选框 --> <!-- /否选择 -->
<template v-else-if="field.type === 'checkbox'"> <template v-else-if="field.type === 'boolean-toggle'">
<UBrowserCheckbox <div class="row items-center no-wrap">
v-model="fieldValue[field.key]" <q-badge class="q-pa-xs">{{ field.label }}</q-badge>
:label="field.label" <q-btn-toggle
@update:model-value="updateValue(field.key, $event)" :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>
</template> </template>
<!-- 基本输入类型的处理 --> <!-- 基本输入类型的处理 -->
@ -37,12 +54,9 @@
<!-- 普通输入框 --> <!-- 普通输入框 -->
<template v-else> <template v-else>
<VariableInput <VariableInput
v-model="fieldValue[field.key]" :model-value="fieldValue[field.key]"
:label="field.label" :label="field.label"
:command="{ :icon="field.icon"
icon: field.icon,
inputType: field.inputType,
}"
@update:model-value="updateValue(field.key, $event)" @update:model-value="updateValue(field.key, $event)"
/> />
</template> </template>
@ -51,16 +65,6 @@
<!-- 数字输入框 --> <!-- 数字输入框 -->
<template v-else-if="field.type === 'numInput'"> <template v-else-if="field.type === 'numInput'">
<NumberInput <NumberInput
v-model="fieldValue[field.key]"
:label="field.label"
:command="{ icon: field.icon }"
@update:model-value="updateValue(field.key, $event)"
/>
</template>
<!-- 文本区域 -->
<template v-else-if="field.type === 'textarea'">
<UBrowserTextarea
v-model="fieldValue[field.key]" v-model="fieldValue[field.key]"
:label="field.label" :label="field.label"
:icon="field.icon" :icon="field.icon"
@ -70,13 +74,20 @@
<!-- 选择框 --> <!-- 选择框 -->
<template v-else-if="field.type === 'select'"> <template v-else-if="field.type === 'select'">
<UBrowserSelect <q-select
v-model="fieldValue[field.key]" :model-value="fieldValue[field.key]"
:label="field.label" :label="field.label"
:icon="field.icon"
:options="field.options" :options="field.options"
dense
filled
emit-value
map-options
@update:model-value="updateValue(field.key, $event)" @update:model-value="updateValue(field.key, $event)"
/> >
<template v-slot:prepend>
<q-icon :name="field.icon" />
</template>
</q-select>
</template> </template>
<!-- Cookie列表 --> <!-- Cookie列表 -->
@ -106,20 +117,39 @@
<!-- 按钮组 --> <!-- 按钮组 -->
<template v-else-if="field.type === 'button-toggle'"> <template v-else-if="field.type === 'button-toggle'">
<UBrowserButtonToggle <div class="row items-center no-wrap">
v-model="fieldValue[field.key]" <q-badge class="q-pa-xs">{{ field.label }}</q-badge>
:label="field.label" <q-btn-toggle
:model-value="fieldValue[field.key]"
:options="field.options" :options="field.options"
dense
flat
no-caps
spread
class="button-group"
@update:model-value="updateValue(field.key, $event)" @update:model-value="updateValue(field.key, $event)"
/> />
</div>
</template> </template>
<!-- 设备尺寸 --> <!-- 设备尺寸 -->
<template v-else-if="field.type === 'device-size'"> <template v-else-if="field.type === 'device-size'">
<UBrowserDeviceSize <div class="row q-col-gutter-sm">
v-model="fieldValue.size" <VariableInput
@update:model-value="updateValue(field.key, $event)" v-model.number="fieldValue.size[key]"
class="col-6"
v-for="key in ['width', 'height']"
:key="key"
label="宽度"
icon="width"
@update:model-value="
updateValue(field.key, {
...fieldValue.size,
[key]: $event,
})
"
/> />
</div>
</template> </template>
<!-- 带参数的函数输入 --> <!-- 带参数的函数输入 -->
@ -139,36 +169,25 @@
</template> </template>
<script> <script>
import { defineComponent } from "vue"; import { defineComponent, computed, ref, onMounted } from "vue";
import { get, set } from "lodash"; import { get, set } from "lodash";
import UBrowserFunctionInput from "./UBrowserFunctionInput.vue"; import UBrowserFunctionInput from "./UBrowserFunctionInput.vue";
import UBrowserCheckbox from "./UBrowserCheckbox.vue";
import UBrowserFileList from "./UBrowserFileList.vue"; import UBrowserFileList from "./UBrowserFileList.vue";
import UBrowserCookieList from "./UBrowserCookieList.vue"; import UBrowserCookieList from "./UBrowserCookieList.vue";
import UBrowserButtonToggle from "./UBrowserButtonToggle.vue";
import UBrowserDeviceSize from "./UBrowserDeviceSize.vue";
import UBrowserNamedParamList from "./UBrowserNamedParamList.vue"; import UBrowserNamedParamList from "./UBrowserNamedParamList.vue";
import UBrowserSelect from "./UBrowserSelect.vue";
import UBrowserDeviceName from "./UBrowserDeviceName.vue"; import UBrowserDeviceName from "./UBrowserDeviceName.vue";
import UBrowserTextarea from "./UBrowserTextarea.vue";
import VariableInput from "components/composer/ui/VariableInput.vue"; import VariableInput from "components/composer/ui/VariableInput.vue";
import UBrowserCheckboxGroup from "./UBrowserCheckboxGroup.vue";
import NumberInput from "components/composer/ui/NumberInput.vue"; import NumberInput from "components/composer/ui/NumberInput.vue";
export default defineComponent({ export default defineComponent({
name: "UBrowserOperation", name: "UBrowserOperation",
components: { components: {
UBrowserFunctionInput, UBrowserFunctionInput,
UBrowserCheckbox,
UBrowserFileList, UBrowserFileList,
UBrowserCookieList, UBrowserCookieList,
UBrowserButtonToggle,
UBrowserDeviceSize,
UBrowserNamedParamList, UBrowserNamedParamList,
UBrowserSelect,
UBrowserDeviceName, UBrowserDeviceName,
UBrowserTextarea,
VariableInput, VariableInput,
UBrowserCheckboxGroup,
NumberInput, NumberInput,
}, },
props: { props: {
@ -186,70 +205,62 @@ export default defineComponent({
}, },
}, },
emits: ["update:configs"], emits: ["update:configs"],
data() { setup(props, { emit }) {
return { const fieldValue = ref({});
fieldValue: {},
}; //
}, onMounted(() => {
created() { props.fields.forEach((field) => {
// const value = get(props.configs[props.action], field.key);
this.fields.forEach((field) => {
const value = get(this.configs[this.action], field.key);
// //
let defaultValue;
if (field.type === "checkbox-group") {
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;
}
this.fieldValue[field.key] = value !== undefined ? value : defaultValue;
});
},
methods: {
updateValue(key, value) {
//
this.fieldValue[key] = value;
//
const newConfigs = { ...this.configs };
if (!newConfigs[this.action]) {
newConfigs[this.action] = {};
}
// 使 lodash set
set(newConfigs[this.action], key, value);
//
this.$emit("update:configs", newConfigs);
},
},
watch: {
//
configs: {
deep: true,
handler() {
this.fields.forEach((field) => {
const value = get(this.configs[this.action], field.key);
if (field.type === "function-with-params") { if (field.type === "function-with-params") {
// function-with-params fieldValue.value.function = value?.function || "";
this.fieldValue.function = fieldValue.value.args = value?.args || [];
value?.function || this.fieldValue.function || "";
this.fieldValue.args = value?.args || this.fieldValue.args || [];
return; return;
} }
if (value !== undefined) {
this.fieldValue[field.key] = value; const defaultValue =
} field.type === "checkbox-group"
? []
: field.type === "checkbox"
? field.defaultValue || false
: field.defaultValue;
fieldValue.value[field.key] = Array.isArray(value)
? value
: defaultValue;
}); });
}, });
},
//
const updateValue = (key, value) => {
fieldValue.value[key] = value;
const newConfigs = { ...props.configs };
if (!newConfigs[props.action]) {
newConfigs[props.action] = {};
}
set(newConfigs[props.action], key, value);
emit("update:configs", newConfigs);
};
return {
fieldValue,
updateValue,
};
}, },
}); });
</script> </script>
<style scoped>
.button-group {
flex: 1;
padding: 0 10px;
}
.button-group :deep(.q-btn) {
min-height: 24px;
font-size: 12px;
}
</style>

View File

@ -1,43 +0,0 @@
<template>
<q-select
:model-value="modelValue"
:label="label"
:options="options"
dense
filled
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>

View File

@ -1,38 +0,0 @@
<template>
<q-input
:model-value="modelValue"
:label="label"
type="textarea"
dense
filled
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>

View File

@ -4,6 +4,7 @@
*/ */
const customComponentGuide = { const customComponentGuide = {
description: "创建自定义命令组件的完整流程", description: "创建自定义命令组件的完整流程",
important: "创建过程中严禁删除、修改任何已有的函数或对象",
steps: { steps: {
"1. Backend Interface": { "1. Backend Interface": {
location: "plugin/lib/quickcomposer/xxx/yyy.js", location: "plugin/lib/quickcomposer/xxx/yyy.js",

View File

@ -4,285 +4,358 @@
* @param {Array} selectedActions 已选择的操作列表 * @param {Array} selectedActions 已选择的操作列表
* @returns {string} 生成的代码 * @returns {string} 生成的代码
*/ */
import { defaultUBrowserConfigs } from "js/composer/ubrowserConfig"; import { stringifyObject, stringifyWithType } from "./formatString";
export function generateUBrowserCode(configs, selectedActions) { // 生成 goto 参数字符串
let code = "utools.ubrowser"; function generateGotoArgs(goto) {
const args = [];
// 基础参数 // URL
// if (configs.useragent.value) { const urlStr = stringifyWithType(goto.url);
// code += `\n .useragent('${configs.useragent.value}')`; args.push(urlStr);
// }
if (configs.goto.url) { // Headers
let gotoOptionsStr = `\n .goto(\n`; if (goto.headers?.Referer?.value || goto.headers?.userAgent?.value) {
gotoOptionsStr += `${configs.goto.url}`; const headers = {};
if (goto.headers.Referer?.value) {
if (configs.goto.headers.Referer || configs.goto.headers.userAgent) { headers.Referer = goto.headers.Referer;
gotoOptionsStr += ",\n{";
if (configs.goto.headers.Referer) {
gotoOptionsStr += `\nReferer: ${configs.goto.headers.Referer}`;
} }
if (configs.goto.headers.userAgent) { if (goto.headers.userAgent?.value) {
gotoOptionsStr += `${ headers.userAgent = goto.headers.userAgent;
configs.goto.headers.Referer ? "," : ""
}\nuserAgent: ${configs.goto.headers.userAgent}`;
} }
gotoOptionsStr += "\n}"; console.log("Headers:", JSON.stringify(headers, null, 2));
args.push(stringifyObject(headers, true));
} }
if (configs.goto.timeout !== 60000) { // Timeout
gotoOptionsStr += `,\n${configs.goto.timeout}`; if (goto.timeout !== 60000) {
args.push(goto.timeout);
} }
gotoOptionsStr += "\n)"; return args.join(", ");
code += gotoOptionsStr;
} }
// 浏览器操作 // 生成 run 参数字符串
selectedActions.forEach((action) => { function generateRunArgs(run) {
const config = configs[action.value]; const options = {};
switch (action.value) { const defaultValues = {
show: true,
center: true,
alwaysOnTop: false,
fullscreen: false,
fullscreenable: true,
resizable: true,
movable: true,
minimizable: true,
maximizable: true,
enableLargerThanScreen: false,
opacity: 1,
};
// 窗口显示控制
if (run.show !== undefined && run.show !== defaultValues.show)
options.show = run.show;
if (run.center !== undefined && run.center !== defaultValues.center)
options.center = run.center;
if (
run.alwaysOnTop !== undefined &&
run.alwaysOnTop !== defaultValues.alwaysOnTop
)
options.alwaysOnTop = run.alwaysOnTop;
if (
run.fullscreen !== undefined &&
run.fullscreen !== defaultValues.fullscreen
)
options.fullscreen = run.fullscreen;
if (
run.fullscreenable !== undefined &&
run.fullscreenable !== defaultValues.fullscreenable
)
options.fullscreenable = run.fullscreenable;
// 窗口尺寸和位置 - 只有设置了值才添加
if (run.width !== undefined && run.width > 0) options.width = run.width;
if (run.height !== undefined && run.height > 0) options.height = run.height;
if (run.x !== undefined && run.x !== 0) options.x = run.x;
if (run.y !== undefined && run.y !== 0) options.y = run.y;
// 最大最小尺寸 - 只有设置了值才添加
if (run.minWidth !== undefined && run.minWidth > 0)
options.minWidth = run.minWidth;
if (run.minHeight !== undefined && run.minHeight > 0)
options.minHeight = run.minHeight;
if (run.maxWidth !== undefined && run.maxWidth > 0)
options.maxWidth = run.maxWidth;
if (run.maxHeight !== undefined && run.maxHeight > 0)
options.maxHeight = run.maxHeight;
// 窗口行为控制
if (run.resizable !== undefined && run.resizable !== defaultValues.resizable)
options.resizable = run.resizable;
if (run.movable !== undefined && run.movable !== defaultValues.movable)
options.movable = run.movable;
if (
run.minimizable !== undefined &&
run.minimizable !== defaultValues.minimizable
)
options.minimizable = run.minimizable;
if (
run.maximizable !== undefined &&
run.maximizable !== defaultValues.maximizable
)
options.maximizable = run.maximizable;
if (
run.enableLargerThanScreen !== undefined &&
run.enableLargerThanScreen !== defaultValues.enableLargerThanScreen
)
options.enableLargerThanScreen = run.enableLargerThanScreen;
// 透明度 - 只有不是1时才添加
if (run.opacity !== undefined && run.opacity !== defaultValues.opacity)
options.opacity = run.opacity;
// 其他参数 - 只有设置了值才添加
if (run.headless) options.headless = run.headless;
if (run.devtools) options.devtools = run.devtools;
if (run.timeout && run.timeout !== 60000) options.timeout = run.timeout;
if (run.proxy) options.proxy = run.proxy;
if (run.viewport) options.viewport = run.viewport;
return Object.keys(options).length ? stringifyObject(options) : "";
}
// 生成操作参数字符串
function generateActionArgs(action, config) {
console.log(
"Generating args for action:",
action,
"config:",
JSON.stringify(config, null, 2)
);
if (!config) return "";
let result;
switch (action) {
case "wait": case "wait":
if (config.type === "time" && config.time) { result = generateWaitArgs(config);
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; break;
case "click": 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) {
code += `\n .screenshot(${config.selector}${
config.savePath ? `, '${config.savePath}'` : ""
})`;
} else if (config.rect) {
code += `\n .screenshot(${JSON.stringify(config.rect)}${
config.savePath ? `, ${config.savePath}` : ""
})`;
}
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") {
let deviceOptionsStr = `\n .device(\n{`;
if (config.size) {
deviceOptionsStr += `\nsize: ${JSON.stringify(config.size)}`;
}
if (config.useragent) {
deviceOptionsStr += `${config.size ? "," : ""}\nuserAgent: ${
config.useragent
}`;
}
deviceOptionsStr += "\n}";
code += deviceOptionsStr + "\n)";
}
break;
case "cookies":
if (config.name) {
code += `\n .cookies(${config.name})`;
} else {
code += `\n .cookies()`;
}
break;
case "setCookies":
if (config.items?.length) {
let cookiesStr = `\n .setCookies([\n`;
config.items.forEach((item, index) => {
cookiesStr += " {";
if (item.name) cookiesStr += `\n name: ${item.name}`;
if (item.value)
cookiesStr += `${item.name ? "," : ""}\n value: ${
item.value
}}`;
if (index < config.items.length - 1) cookiesStr += ",";
cookiesStr += "\n";
});
cookiesStr += " ])";
code += cookiesStr;
}
break;
case "removeCookies":
if (config.name) {
code += `\n .removeCookies(${config.name})`;
}
break;
case "clearCookies":
code += `\n .clearCookies(${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 "mousedown":
case "mouseup": case "mouseup":
if (config.selector) {
code += `\n .${action.value}(${config.selector})`;
}
break;
case "file":
if (config.selector && config.files?.length) {
let filesStr = `\n .file(${config.selector}, [\n`;
config.files.forEach((file, index) => {
filesStr += ` ${file}`;
if (index < config.files.length - 1) filesStr += ",\n";
});
filesStr += "\n ])";
code += filesStr;
}
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": case "focus":
if (config.selector) { result = stringifyWithType(config.selector);
code += `\n .focus(${config.selector})`; break;
} case "css":
case "paste":
result = stringifyWithType(config.value);
break;
case "press":
result = generatePressArgs(config);
break;
case "screenshot":
result = generateScreenshotArgs(config);
break;
case "pdf":
result = generatePdfArgs(config);
break;
case "device":
result = generateDeviceArgs(config);
break;
case "cookies":
case "removeCookies":
result = stringifyWithType(config.name);
break;
case "setCookies":
result = generateSetCookiesArgs(config);
break;
case "evaluate":
result = generateEvaluateArgs(config);
break;
case "when":
result = generateWhenArgs(config);
break;
case "file":
result = generateFileArgs(config);
break;
case "value":
result = generateValueArgs(config);
break;
case "check":
result = generateCheckArgs(config);
break; break;
case "scroll": case "scroll":
if (config.type === "element" && config.selector) { result = generateScrollArgs(config);
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; break;
case "download": case "download":
if (config.url) { result = generateDownloadArgs(config);
code += `\n .download(${config.url}${
config.savePath ? `, ${config.savePath}` : ""
})`;
}
break; break;
case "hide":
case "show":
code += `\n .${action.value}()`;
break;
case "devTools": case "devTools":
if (config.mode) { result = stringifyWithType(config.mode);
code += `\n .devTools(${config.mode})`;
} else {
code += `\n .devTools()`;
}
break; break;
default:
result = "";
} }
console.log(
"Generated args for action:",
action,
"result:",
JSON.stringify(result)
);
return result;
}
// 生成 wait 参数字符串
function generateWaitArgs(config) {
switch (config.type) {
case "selector":
return stringifyWithType(config.selector);
case "function":
return config.function;
case "time":
return config.time;
default:
return "";
}
}
// 生成 press 参数字符串
function generatePressArgs(config) {
const args = [stringifyWithType(config.key)];
if (config.modifiers?.length) {
args.push(JSON.stringify(config.modifiers));
}
return args.join(", ");
}
// 生成 screenshot 参数字符串
function generateScreenshotArgs(config) {
const args = [];
if (config.rect) {
args.push(stringifyObject(config.rect));
} else if (config.selector) {
args.push(stringifyWithType(config.selector));
}
if (config.savePath) {
args.push(stringifyWithType(config.savePath));
}
return args.join(", ");
}
// 生成 pdf 参数字符串
function generatePdfArgs(config) {
const args = [];
if (config.savePath) {
args.push(stringifyWithType(config.savePath));
}
if (config.options) {
args.push(stringifyObject(config.options));
}
return args.join(", ");
}
// 生成 device 参数字符串
function generateDeviceArgs(config) {
if (config.type === "preset") {
return stringifyWithType(config.deviceName);
} else {
const options = {};
if (config.size) options.size = config.size;
if (config.useragent) options.userAgent = config.useragent;
return stringifyObject(options);
}
}
// 生成 setCookies 参数字符串
function generateSetCookiesArgs(config) {
if (!config.items?.length) return "[]";
return stringifyObject(config.items);
}
// 生成 evaluate 参数字符串
function generateEvaluateArgs(config) {
const args = [config.function];
if (config.args?.length) {
args.push(...config.args.map(stringifyWithType));
}
return args.join(", ");
}
// 生成 when 参数字符串
function generateWhenArgs(config) {
if (config.type === "function") {
return config.function;
} else {
return stringifyWithType(config.selector);
}
}
// 生成 file 参数字符串
function generateFileArgs(config) {
const args = [stringifyWithType(config.selector)];
if (config.files) {
args.push(stringifyObject(config.files));
}
return args.join(", ");
}
// 生成 value 参数字符串
function generateValueArgs(config) {
return `${stringifyWithType(config.selector)}, ${stringifyWithType(
config.value
)}`;
}
// 生成 check 参数字符串
function generateCheckArgs(config) {
return `${stringifyWithType(config.selector)}, ${config.checked}`;
}
// 生成 scroll 参数字符串
function generateScrollArgs(config) {
if (config.type === "element") {
return stringifyWithType(config.selector);
} else {
if (config.x !== undefined) {
return `${config.x}, ${config.y}`;
} else {
return String(config.y);
}
}
}
// 生成 download 参数字符串
function generateDownloadArgs(config) {
const args = [stringifyWithType(config.url)];
if (config.savePath) {
args.push(stringifyWithType(config.savePath));
}
return args.join(", ");
}
// 生成完整的 ubrowser 代码
export function generateUBrowserCode(configs, selectedActions) {
const lines = [];
const indent = " ";
// 添加 goto 参数
if (configs.goto) {
const gotoArgs = generateGotoArgs(configs.goto);
lines.push(`${indent}goto(${gotoArgs}),`);
}
// 添加选中的操作
if (selectedActions?.length) {
selectedActions.forEach((action) => {
const args = generateActionArgs(action.value, configs[action.value]);
lines.push(`${indent}${action.value}(${args}),`);
}); });
// 运行参数
const runOptions = {};
Object.entries(configs.run).forEach(([key, value]) => {
if (
value !== undefined &&
value !== null &&
value !== defaultUBrowserConfigs.run[key]
) {
runOptions[key] = value;
} }
});
code += `\n .run(${ // 添加 run 参数
Object.keys(runOptions).length const runArgs = generateRunArgs(configs.run || {});
? `\n${JSON.stringify(runOptions, null, 2).replace(/\n/g, "\n ")}` const runLine = runArgs ? `${indent}run(${runArgs})` : `${indent}run()`;
: "" lines.push(runLine);
})`;
return code; // 生成最终代码
return `utools.ubrowser\n${lines.join("\n")}`;
} }

View File

@ -76,7 +76,7 @@ export const ubrowserOperationConfigs = [
key: "value", key: "value",
label: "注入的CSS样式", label: "注入的CSS样式",
icon: "style", icon: "style",
type: "textarea", type: "varInput",
}, },
], ],
icon: "style", icon: "style",
@ -424,7 +424,7 @@ export const ubrowserOperationConfigs = [
{ {
key: "checked", key: "checked",
label: "选中状态", label: "选中状态",
type: "checkbox", type: "boolean-toggle",
defaultValue: false, defaultValue: false,
width: 4, width: 4,
}, },
@ -564,9 +564,6 @@ const defaultUBrowserRunConfigs = {
// ubrowser 默认配置 基础参数-浏览器操作-运行参数 // ubrowser 默认配置 基础参数-浏览器操作-运行参数
export const defaultUBrowserConfigs = { export const defaultUBrowserConfigs = {
// 基础参数 // 基础参数
useragent: {
value: "",
},
goto: { goto: {
url: { url: {
value: "", value: "",
@ -593,82 +590,183 @@ export const defaultUBrowserConfigs = {
timeout: 60000, timeout: 60000,
}, },
click: { click: {
selector: "", selector: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
css: { css: {
value: {
value: "", value: "",
isString: true,
__varInputVal__: true,
},
}, },
press: { press: {
key: "", key: {
value: "",
isString: true,
__varInputVal__: true,
},
modifiers: [], modifiers: [],
}, },
paste: { paste: {
text: "", text: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
screenshot: { screenshot: {
selector: "", selector: {
value: "",
isString: true,
__varInputVal__: true,
},
rect: { x: 0, y: 0, width: 0, height: 0 }, rect: { x: 0, y: 0, width: 0, height: 0 },
savePath: "", savePath: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
pdf: { pdf: {
options: { options: {
marginsType: 0, marginsType: 0,
pageSize: "A4", pageSize: "A4",
}, },
savePath: "", savePath: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
device: { device: {
size: { width: 1280, height: 800 }, size: { width: 1280, height: 800 },
useragent: "", useragent: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
cookies: { cookies: {
name: "", name: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
setCookies: { setCookies: {
items: [{ name: "", value: "" }], items: [
{
name: {
value: "",
isString: true,
__varInputVal__: true,
},
value: {
value: "",
isString: true,
__varInputVal__: true,
},
},
],
}, },
removeCookies: { removeCookies: {
name: "", name: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
clearCookies: { clearCookies: {
url: "", url: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
evaluate: { evaluate: {
function: "", function: "",
params: [], params: [],
}, },
when: { when: {
condition: "", condition: {
value: "",
isString: false,
__varInputVal__: true,
},
}, },
mousedown: { mousedown: {
selector: "", selector: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
mouseup: { mouseup: {
selector: "", selector: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
file: { file: {
selector: "", selector: {
value: "",
isString: true,
__varInputVal__: true,
},
files: [], files: [],
}, },
value: { value: {
selector: "", selector: {
value: "", value: "",
isString: true,
__varInputVal__: true,
},
value: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
check: { check: {
selector: "", selector: {
value: "",
isString: true,
__varInputVal__: true,
},
checked: false, checked: false,
}, },
focus: { focus: {
selector: "", selector: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
scroll: { scroll: {
target: "", target: {
value: "",
isString: true,
__varInputVal__: true,
},
x: 0, x: 0,
y: 0, y: 0,
}, },
download: { download: {
url: "", url: {
savePath: "", value: "",
isString: true,
__varInputVal__: true,
},
savePath: {
value: "",
isString: true,
__varInputVal__: true,
},
}, },
// 运行参数 // 运行参数
run: defaultUBrowserRunConfigs, run: defaultUBrowserRunConfigs,