mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-29 20:32:44 +08:00
完善ubrowser可视化编排
This commit is contained in:
parent
d0223d3e61
commit
a22e1e23e9
@ -1,24 +1,24 @@
|
||||
// 定义命令图标映射
|
||||
export const commandIcons = {
|
||||
'open': 'folder_open',
|
||||
'locate': 'location_on',
|
||||
'visit': 'language',
|
||||
'utools.ubrowser.goto': 'public',
|
||||
'system': 'terminal',
|
||||
'copyTo': 'content_copy',
|
||||
'message': 'message',
|
||||
'alert': 'warning',
|
||||
'send': 'send',
|
||||
'utools.redirect': 'alt_route',
|
||||
'quickcommand.sleep': 'schedule',
|
||||
'keyTap': 'keyboard'
|
||||
}
|
||||
open: "folder_open",
|
||||
locate: "location_on",
|
||||
visit: "language",
|
||||
"utools.ubrowser.goto": "public",
|
||||
system: "terminal",
|
||||
copyTo: "content_copy",
|
||||
message: "message",
|
||||
alert: "warning",
|
||||
send: "send",
|
||||
"utools.redirect": "alt_route",
|
||||
"quickcommand.sleep": "schedule",
|
||||
keyTap: "keyboard",
|
||||
};
|
||||
|
||||
// 定义命令分类
|
||||
export const commandCategories = [
|
||||
{
|
||||
label: '文件操作',
|
||||
icon: 'folder',
|
||||
label: "文件操作",
|
||||
icon: "folder",
|
||||
commands: [
|
||||
{
|
||||
value: "open",
|
||||
@ -29,12 +29,12 @@ export const commandCategories = [
|
||||
value: "locate",
|
||||
label: "在文件管理器中定位文件",
|
||||
desc: "要在文件管理器里显示的文件路径",
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '网络操作',
|
||||
icon: 'language',
|
||||
label: "网络操作",
|
||||
icon: "language",
|
||||
commands: [
|
||||
{
|
||||
value: "visit",
|
||||
@ -50,13 +50,13 @@ export const commandCategories = [
|
||||
value: "ubrowser",
|
||||
label: "UBrowser浏览器操作",
|
||||
desc: "配置UBrowser浏览器操作",
|
||||
hasUBrowserEditor: true
|
||||
}
|
||||
]
|
||||
hasUBrowserEditor: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '系统操作',
|
||||
icon: 'computer',
|
||||
label: "系统操作",
|
||||
icon: "computer",
|
||||
commands: [
|
||||
{
|
||||
value: "system",
|
||||
@ -67,12 +67,12 @@ export const commandCategories = [
|
||||
value: "copyTo",
|
||||
label: "将内容写入剪贴板",
|
||||
desc: "要写入剪切板的内容",
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '消息通知',
|
||||
icon: 'notifications',
|
||||
label: "消息通知",
|
||||
icon: "notifications",
|
||||
commands: [
|
||||
{
|
||||
value: "message",
|
||||
@ -88,12 +88,12 @@ export const commandCategories = [
|
||||
value: "send",
|
||||
label: "发送文本到活动窗口",
|
||||
desc: "要发送到窗口的文本内容",
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '其他功能',
|
||||
icon: 'more_horiz',
|
||||
label: "其他功能",
|
||||
icon: "more_horiz",
|
||||
commands: [
|
||||
{
|
||||
value: "utools.redirect",
|
||||
@ -104,36 +104,91 @@ export const commandCategories = [
|
||||
value: "quickcommand.sleep",
|
||||
label: "添加延时",
|
||||
desc: "延迟的毫秒数",
|
||||
}
|
||||
]
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '按键操作',
|
||||
icon: 'keyboard',
|
||||
label: "按键操作",
|
||||
icon: "keyboard",
|
||||
commands: [
|
||||
{
|
||||
value: "keyTap",
|
||||
label: "模拟按键",
|
||||
desc: "模拟键盘按键",
|
||||
hasKeyRecorder: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
hasKeyRecorder: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// 定义哪些命令可以产生输出
|
||||
export const commandsWithOutput = {
|
||||
'system': true,
|
||||
'open': true,
|
||||
'locate': true,
|
||||
'copyTo': true,
|
||||
'ubrowser': true,
|
||||
}
|
||||
system: true,
|
||||
open: true,
|
||||
locate: true,
|
||||
copyTo: true,
|
||||
ubrowser: true,
|
||||
};
|
||||
|
||||
// 定义哪些命令可以接收输出
|
||||
export const commandsAcceptOutput = {
|
||||
'message': true,
|
||||
'alert': true,
|
||||
'send': true,
|
||||
'copyTo': true,
|
||||
}
|
||||
message: true,
|
||||
alert: true,
|
||||
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" },
|
||||
];
|
||||
|
@ -1,86 +1,115 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<!-- UserAgent -->
|
||||
<!-- 基础配置 -->
|
||||
<div class="col-12">
|
||||
<UBrowserInput
|
||||
:value="configs.useragent.value"
|
||||
@update:modelValue="updateConfig('useragent.value', $event)"
|
||||
label="UserAgent"
|
||||
icon="person"
|
||||
/>
|
||||
<q-input
|
||||
v-model="localConfigs.goto.url"
|
||||
label="网址"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="link" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
|
||||
<!-- URL -->
|
||||
<!-- 超时配置 -->
|
||||
<div class="col-12">
|
||||
<UBrowserInput
|
||||
:value="configs.goto.url"
|
||||
@update:modelValue="updateConfig('goto.url', $event)"
|
||||
label="URL"
|
||||
icon="link"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Headers -->
|
||||
<div class="col-12">
|
||||
<div class="text-subtitle2 q-mb-sm">请求头</div>
|
||||
<UBrowserInput
|
||||
:value="configs.goto.headers.Referer"
|
||||
@update:modelValue="updateConfig('goto.headers.Referer', $event)"
|
||||
label="Referer"
|
||||
icon="link"
|
||||
class="q-mb-sm"
|
||||
/>
|
||||
<UBrowserInput
|
||||
:value="configs.goto.headers.userAgent"
|
||||
@update:modelValue="updateConfig('goto.headers.userAgent', $event)"
|
||||
label="User-Agent"
|
||||
icon="person"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Timeout -->
|
||||
<div class="col-12">
|
||||
<UBrowserInput
|
||||
:value="configs.goto.timeout"
|
||||
@update:modelValue="updateConfig('goto.timeout', $event)"
|
||||
<q-input
|
||||
v-model.number="localConfigs.goto.timeout"
|
||||
type="number"
|
||||
label="超时时间(ms)"
|
||||
icon="timer"
|
||||
/>
|
||||
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">
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
v-model="localConfigs.goto.headers.Referer"
|
||||
label="Referer"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="link" />
|
||||
</template>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import UBrowserInput from './operations/UBrowserInput.vue';
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UBrowserBasic',
|
||||
components: {
|
||||
UBrowserInput
|
||||
},
|
||||
name: "UBrowserBasic",
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:configs"],
|
||||
data() {
|
||||
return {
|
||||
localConfigs: {
|
||||
goto: {
|
||||
url: "",
|
||||
headers: {
|
||||
Referer: "",
|
||||
userAgent: "",
|
||||
},
|
||||
timeout: 60000,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 初始化本地配置
|
||||
this.localConfigs = JSON.parse(JSON.stringify(this.configs));
|
||||
},
|
||||
emits: ['update:configs'],
|
||||
methods: {
|
||||
updateConfig(path, value) {
|
||||
const newConfigs = { ...this.configs };
|
||||
const keys = path.split('.');
|
||||
let current = newConfigs;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
current[keys[i]] = { ...current[keys[i]] };
|
||||
current = current[keys[i]];
|
||||
}
|
||||
|
||||
current[keys[keys.length - 1]] = value;
|
||||
this.$emit('update:configs', newConfigs);
|
||||
}
|
||||
}
|
||||
updateConfigs() {
|
||||
this.$emit(
|
||||
"update:configs",
|
||||
JSON.parse(JSON.stringify(this.localConfigs))
|
||||
);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
configs: {
|
||||
deep: true,
|
||||
handler(newConfigs) {
|
||||
this.localConfigs = JSON.parse(JSON.stringify(newConfigs));
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -11,46 +11,18 @@
|
||||
class="ubrowser-stepper"
|
||||
>
|
||||
<!-- 基础参数步骤 -->
|
||||
<q-step
|
||||
:name="1"
|
||||
title="基础参数"
|
||||
icon="settings"
|
||||
:done="step > 1"
|
||||
>
|
||||
<UBrowserBasic
|
||||
:configs="configs"
|
||||
@update:configs="updateConfigs"
|
||||
/>
|
||||
<q-step :name="1" title="基础参数" icon="settings" :done="step > 1">
|
||||
<UBrowserBasic :configs="configs" @update:configs="updateConfigs" />
|
||||
</q-step>
|
||||
|
||||
<!-- 浏览器操作步骤 -->
|
||||
<q-step
|
||||
:name="2"
|
||||
title="浏览器操作"
|
||||
icon="touch_app"
|
||||
:done="step > 2"
|
||||
>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12">
|
||||
<q-select
|
||||
v-model="selectedActions"
|
||||
:options="availableActions"
|
||||
multiple
|
||||
use-chips
|
||||
outlined
|
||||
dense
|
||||
label="选择操作"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<UBrowserOperations
|
||||
:configs="configs"
|
||||
@update:configs="updateConfigs"
|
||||
v-model:selected-actions="selectedActions"
|
||||
@remove-action="removeAction"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-step :name="2" title="浏览器操作" icon="touch_app" :done="step > 2">
|
||||
<UBrowserOperations
|
||||
:configs="configs"
|
||||
@update:configs="updateConfigs"
|
||||
v-model:selected-actions="selectedActions"
|
||||
@remove-action="removeAction"
|
||||
/>
|
||||
</q-step>
|
||||
|
||||
<!-- 运行参数步骤 -->
|
||||
@ -60,35 +32,55 @@
|
||||
icon="settings_applications"
|
||||
class="q-pb-md"
|
||||
>
|
||||
<UBrowserRun
|
||||
:configs="configs"
|
||||
@update:configs="updateConfigs"
|
||||
/>
|
||||
<UBrowserRun :configs="configs" @update:configs="updateConfigs" />
|
||||
</q-step>
|
||||
</q-stepper>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ubrowser-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ubrowser-stepper {
|
||||
box-shadow: none;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.body--dark .ubrowser-stepper {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.ubrowser-stepper :deep(.q-stepper__header) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ubrowser-stepper :deep(.q-stepper__step-inner) {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import UBrowserBasic from './UBrowserBasic.vue';
|
||||
import UBrowserOperations from './UBrowserOperations.vue';
|
||||
import UBrowserRun from './UBrowserRun.vue';
|
||||
import { defineComponent } from "vue";
|
||||
import UBrowserBasic from "./UBrowserBasic.vue";
|
||||
import UBrowserOperations from "./UBrowserOperations.vue";
|
||||
import UBrowserRun from "./UBrowserRun.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UBrowserEditor',
|
||||
name: "UBrowserEditor",
|
||||
components: {
|
||||
UBrowserBasic,
|
||||
UBrowserOperations,
|
||||
UBrowserRun
|
||||
UBrowserRun,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
emits: ["update:modelValue"],
|
||||
data() {
|
||||
return {
|
||||
step: 1,
|
||||
@ -96,98 +88,98 @@ export default defineComponent({
|
||||
configs: {
|
||||
// 基础参数
|
||||
useragent: {
|
||||
value: ''
|
||||
value: "",
|
||||
},
|
||||
goto: {
|
||||
url: '',
|
||||
url: "",
|
||||
headers: {
|
||||
Referer: '',
|
||||
userAgent: ''
|
||||
Referer: "",
|
||||
userAgent: "",
|
||||
},
|
||||
timeout: 60000
|
||||
timeout: 60000,
|
||||
},
|
||||
// 浏览器操作
|
||||
wait: {
|
||||
value: '',
|
||||
timeout: 60000
|
||||
value: "",
|
||||
timeout: 60000,
|
||||
},
|
||||
click: {
|
||||
selector: ''
|
||||
selector: "",
|
||||
},
|
||||
css: {
|
||||
value: ''
|
||||
value: "",
|
||||
},
|
||||
press: {
|
||||
key: '',
|
||||
modifiers: []
|
||||
key: "",
|
||||
modifiers: [],
|
||||
},
|
||||
paste: {
|
||||
text: ''
|
||||
text: "",
|
||||
},
|
||||
screenshot: {
|
||||
selector: '',
|
||||
selector: "",
|
||||
rect: { x: 0, y: 0, width: 0, height: 0 },
|
||||
savePath: ''
|
||||
savePath: "",
|
||||
},
|
||||
pdf: {
|
||||
options: {
|
||||
marginsType: 0,
|
||||
pageSize: 'A4'
|
||||
pageSize: "A4",
|
||||
},
|
||||
savePath: ''
|
||||
savePath: "",
|
||||
},
|
||||
device: {
|
||||
size: { width: 1280, height: 800 },
|
||||
useragent: ''
|
||||
useragent: "",
|
||||
},
|
||||
cookies: {
|
||||
name: ''
|
||||
name: "",
|
||||
},
|
||||
setCookies: {
|
||||
items: [{ name: '', value: '' }]
|
||||
items: [{ name: "", value: "" }],
|
||||
},
|
||||
removeCookies: {
|
||||
name: ''
|
||||
name: "",
|
||||
},
|
||||
clearCookies: {
|
||||
url: ''
|
||||
url: "",
|
||||
},
|
||||
evaluate: {
|
||||
function: '',
|
||||
params: []
|
||||
function: "",
|
||||
params: [],
|
||||
},
|
||||
when: {
|
||||
condition: ''
|
||||
condition: "",
|
||||
},
|
||||
mousedown: {
|
||||
selector: ''
|
||||
selector: "",
|
||||
},
|
||||
mouseup: {
|
||||
selector: ''
|
||||
selector: "",
|
||||
},
|
||||
file: {
|
||||
selector: '',
|
||||
files: []
|
||||
selector: "",
|
||||
files: [],
|
||||
},
|
||||
value: {
|
||||
selector: '',
|
||||
value: ''
|
||||
selector: "",
|
||||
value: "",
|
||||
},
|
||||
check: {
|
||||
selector: '',
|
||||
checked: false
|
||||
selector: "",
|
||||
checked: false,
|
||||
},
|
||||
focus: {
|
||||
selector: ''
|
||||
selector: "",
|
||||
},
|
||||
scroll: {
|
||||
target: '',
|
||||
target: "",
|
||||
x: 0,
|
||||
y: 0
|
||||
y: 0,
|
||||
},
|
||||
download: {
|
||||
url: '',
|
||||
savePath: ''
|
||||
url: "",
|
||||
savePath: "",
|
||||
},
|
||||
// 运行参数
|
||||
run: {
|
||||
@ -209,54 +201,42 @@ export default defineComponent({
|
||||
fullscreen: false,
|
||||
fullscreenable: true,
|
||||
enableLargerThanScreen: false,
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
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,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
availableActions() {
|
||||
return [
|
||||
{ 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' }
|
||||
];
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateConfigs(newConfigs) {
|
||||
this.configs = newConfigs;
|
||||
},
|
||||
removeAction(action) {
|
||||
const index = this.selectedActions.findIndex(a => a.value === action.value);
|
||||
const index = this.selectedActions.findIndex(
|
||||
(a) => a.value === action.value
|
||||
);
|
||||
if (index > -1) {
|
||||
this.selectedActions.splice(index, 1);
|
||||
}
|
||||
},
|
||||
generateCode() {
|
||||
let code = 'utools.ubrowser';
|
||||
let code = "utools.ubrowser";
|
||||
|
||||
// 基础参数
|
||||
if (this.configs.useragent.value) {
|
||||
@ -271,216 +251,235 @@ export default defineComponent({
|
||||
}
|
||||
if (this.configs.goto.headers.userAgent) {
|
||||
gotoOptions.headers = gotoOptions.headers || {};
|
||||
gotoOptions.headers['User-Agent'] = this.configs.goto.headers.userAgent;
|
||||
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)}` : ''})`;
|
||||
code += `.goto('${this.configs.goto.url}'${
|
||||
Object.keys(gotoOptions).length
|
||||
? `, ${JSON.stringify(gotoOptions)}`
|
||||
: ""
|
||||
})`;
|
||||
}
|
||||
|
||||
// 浏览器操作
|
||||
this.selectedActions.forEach(action => {
|
||||
this.selectedActions.forEach((action) => {
|
||||
const config = this.configs[action.value];
|
||||
switch (action.value) {
|
||||
case 'wait':
|
||||
case "wait":
|
||||
if (config.value) {
|
||||
code += `.wait('${config.value}'${config.timeout !== 60000 ? `, ${config.timeout}` : ''})`;
|
||||
code += `.wait('${config.value}'${
|
||||
config.timeout !== 60000 ? `, ${config.timeout}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'click':
|
||||
case "click":
|
||||
if (config.selector) {
|
||||
code += `.click('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'css':
|
||||
case "css":
|
||||
if (config.value) {
|
||||
code += `.css('${config.value}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'press':
|
||||
case "press":
|
||||
if (config.key) {
|
||||
const modifiers = config.modifiers.length ? `, ${JSON.stringify(config.modifiers)}` : '';
|
||||
const modifiers = config.modifiers.length
|
||||
? `, ${JSON.stringify(config.modifiers)}`
|
||||
: "";
|
||||
code += `.press('${config.key}'${modifiers})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'paste':
|
||||
case "paste":
|
||||
if (config.text) {
|
||||
code += `.paste('${config.text}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'screenshot':
|
||||
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)}` : ''})`;
|
||||
code += `.screenshot('${config.savePath}'${
|
||||
Object.keys(options).length
|
||||
? `, ${JSON.stringify(options)}`
|
||||
: ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'pdf':
|
||||
case "pdf":
|
||||
if (config.savePath) {
|
||||
code += `.pdf('${config.savePath}'${config.options ? `, ${JSON.stringify(config.options)}` : ''})`;
|
||||
code += `.pdf('${config.savePath}'${
|
||||
config.options ? `, ${JSON.stringify(config.options)}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'device':
|
||||
case "device":
|
||||
if (config.size.width && config.size.height) {
|
||||
const options = {
|
||||
size: config.size
|
||||
size: config.size,
|
||||
};
|
||||
if (config.useragent) options.useragent = config.useragent;
|
||||
code += `.device(${JSON.stringify(options)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'cookies':
|
||||
case "cookies":
|
||||
if (config.name) {
|
||||
code += `.cookies('${config.name}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'setCookies':
|
||||
case "setCookies":
|
||||
if (config.items?.length) {
|
||||
code += `.setCookies(${JSON.stringify(config.items)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'removeCookies':
|
||||
case "removeCookies":
|
||||
if (config.name) {
|
||||
code += `.removeCookies('${config.name}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'clearCookies':
|
||||
code += `.clearCookies(${config.url ? `'${config.url}'` : ''})`;
|
||||
case "clearCookies":
|
||||
code += `.clearCookies(${config.url ? `'${config.url}'` : ""})`;
|
||||
break;
|
||||
|
||||
case 'evaluate':
|
||||
case "evaluate":
|
||||
if (config.function) {
|
||||
const params = config.params.length ? `, ${JSON.stringify(config.params)}` : '';
|
||||
const params = config.params.length
|
||||
? `, ${JSON.stringify(config.params)}`
|
||||
: "";
|
||||
code += `.evaluate(\`${config.function}\`${params})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'when':
|
||||
case "when":
|
||||
if (config.condition) {
|
||||
code += `.when('${config.condition}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'mousedown':
|
||||
case 'mouseup':
|
||||
case "mousedown":
|
||||
case "mouseup":
|
||||
if (config.selector) {
|
||||
code += `.${action.value}('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'file':
|
||||
case "file":
|
||||
if (config.selector && config.files?.length) {
|
||||
code += `.file('${config.selector}', ${JSON.stringify(config.files)})`;
|
||||
code += `.file('${config.selector}', ${JSON.stringify(
|
||||
config.files
|
||||
)})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'value':
|
||||
case "value":
|
||||
if (config.selector) {
|
||||
code += `.value('${config.selector}', '${config.value}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'check':
|
||||
case "check":
|
||||
if (config.selector) {
|
||||
code += `.check('${config.selector}'${config.checked !== undefined ? `, ${config.checked}` : ''})`;
|
||||
code += `.check('${config.selector}'${
|
||||
config.checked !== undefined ? `, ${config.checked}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'focus':
|
||||
case "focus":
|
||||
if (config.selector) {
|
||||
code += `.focus('${config.selector}')`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'scroll':
|
||||
if (config.x !== undefined || config.y !== undefined) {
|
||||
const options = {};
|
||||
if (config.target) options.target = config.target;
|
||||
if (config.x !== undefined) options.x = config.x;
|
||||
if (config.y !== undefined) options.y = config.y;
|
||||
code += `.scroll(${JSON.stringify(options)})`;
|
||||
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':
|
||||
case "download":
|
||||
if (config.url) {
|
||||
code += `.download('${config.url}'${config.savePath ? `, '${config.savePath}'` : ''})`;
|
||||
code += `.download('${config.url}'${
|
||||
config.savePath ? `, '${config.savePath}'` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'hide':
|
||||
case 'show':
|
||||
case 'devTools':
|
||||
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) {
|
||||
if (
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
value !== this.defaultRunConfigs[key]
|
||||
) {
|
||||
runOptions[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
code += `.run(${Object.keys(runOptions).length ? JSON.stringify(runOptions) : ''})`;
|
||||
code += `.run(${
|
||||
Object.keys(runOptions).length ? JSON.stringify(runOptions) : ""
|
||||
})`;
|
||||
|
||||
this.$emit('update:modelValue', code);
|
||||
}
|
||||
this.$emit("update:modelValue", code);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
configs: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.generateCode();
|
||||
}
|
||||
},
|
||||
},
|
||||
selectedActions: {
|
||||
handler() {
|
||||
this.generateCode();
|
||||
}
|
||||
},
|
||||
},
|
||||
step: {
|
||||
handler() {
|
||||
this.generateCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ubrowser-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ubrowser-stepper {
|
||||
box-shadow: none;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.body--dark .ubrowser-stepper {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.ubrowser-stepper :deep(.q-stepper__header) {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,54 +1,76 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12">
|
||||
<q-list separator class="operation-list">
|
||||
<!-- 操作选择网格 -->
|
||||
<div class="row q-col-gutter-xs">
|
||||
<div
|
||||
v-for="action in availableActions"
|
||||
:key="action.value"
|
||||
class="col-2"
|
||||
>
|
||||
<q-card
|
||||
flat
|
||||
bordered
|
||||
class="action-card cursor-pointer"
|
||||
:class="{
|
||||
'action-selected': selectedActions.some(
|
||||
(a) => a.value === action.value
|
||||
),
|
||||
}"
|
||||
@click="toggleAction(action)"
|
||||
>
|
||||
<div class="q-pa-xs text-caption text-wrap text-center">
|
||||
{{ action.label }}
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已选操作列表 -->
|
||||
<q-list separator class="operation-list q-mt-md">
|
||||
<div
|
||||
v-for="(action, index) in selectedActions"
|
||||
:key="action.value"
|
||||
:key="action.id"
|
||||
class="operation-item"
|
||||
>
|
||||
<div class="row items-center justify-between">
|
||||
<div class="row items-center">
|
||||
<q-icon
|
||||
:name="getActionIcon(action.value)"
|
||||
size="xs"
|
||||
class="q-mx-sm"
|
||||
<q-chip
|
||||
square
|
||||
removable
|
||||
@remove="$emit('remove-action', action)"
|
||||
class="text-caption q-mx-none q-mb-sm"
|
||||
>
|
||||
<q-avatar color="primary">
|
||||
<q-icon
|
||||
color="white"
|
||||
:name="getActionIcon(action.value)"
|
||||
size="14px"
|
||||
/>
|
||||
</q-avatar>
|
||||
<div class="q-mx-sm">{{ action.label }}</div>
|
||||
</q-chip>
|
||||
<div class="row items-start q-gutter-xs">
|
||||
<q-btn
|
||||
round
|
||||
dense
|
||||
color="primary"
|
||||
icon="north"
|
||||
v-show="index !== 0"
|
||||
@click="moveAction(index, -1)"
|
||||
size="xs"
|
||||
class="q-mb-xs move-btn"
|
||||
/>
|
||||
<q-btn
|
||||
round
|
||||
dense
|
||||
color="primary"
|
||||
icon="south"
|
||||
v-show="index !== selectedActions.length - 1"
|
||||
@click="moveAction(index, 1)"
|
||||
size="xs"
|
||||
class="move-btn"
|
||||
/>
|
||||
<div class="text-subtitle1">{{ action.label }}</div>
|
||||
<div class="row items-center q-ml-md">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
icon="north"
|
||||
:disable="index === 0"
|
||||
@click="moveAction(index, -1)"
|
||||
size="xs"
|
||||
class="q-mb-xs move-btn"
|
||||
/>
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
icon="south"
|
||||
:disable="index === selectedActions.length - 1"
|
||||
@click="moveAction(index, 1)"
|
||||
size="xs"
|
||||
class="move-btn"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
icon="delete"
|
||||
color="negative"
|
||||
size="sm"
|
||||
@click="$emit('remove-action', action)"
|
||||
class="delete-btn"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="getOperationConfig(action.value)">
|
||||
<UBrowserOperation
|
||||
@ -66,6 +88,10 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import {
|
||||
ubrowserActionIcons,
|
||||
ubrowserAvailableActions,
|
||||
} from "../composerConfig";
|
||||
import UBrowserOperation from "./operations/UBrowserOperation.vue";
|
||||
|
||||
export default defineComponent({
|
||||
@ -83,7 +109,12 @@ export default defineComponent({
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["remove-action", "update:selectedActions"],
|
||||
emits: ["remove-action", "update:selectedActions", "update:configs"],
|
||||
computed: {
|
||||
availableActions() {
|
||||
return ubrowserAvailableActions;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
moveAction(index, direction) {
|
||||
const newIndex = index + direction;
|
||||
@ -96,31 +127,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
getActionIcon(action) {
|
||||
const iconMap = {
|
||||
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",
|
||||
};
|
||||
return iconMap[action] || "touch_app";
|
||||
return ubrowserActionIcons[action] || "touch_app";
|
||||
},
|
||||
getOperationConfig(action) {
|
||||
const configs = {
|
||||
@ -130,6 +137,7 @@ export default defineComponent({
|
||||
label: "等待时间(ms)或CSS选择器",
|
||||
icon: "timer",
|
||||
type: "input",
|
||||
width: 8,
|
||||
},
|
||||
{
|
||||
key: "timeout",
|
||||
@ -137,6 +145,7 @@ export default defineComponent({
|
||||
icon: "timer_off",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 4,
|
||||
},
|
||||
],
|
||||
click: [
|
||||
@ -156,9 +165,16 @@ export default defineComponent({
|
||||
},
|
||||
],
|
||||
press: [
|
||||
{ key: "key", label: "按键", icon: "keyboard", type: "input" },
|
||||
{
|
||||
key: "key",
|
||||
label: "按键",
|
||||
icon: "keyboard",
|
||||
type: "input",
|
||||
width: 5,
|
||||
},
|
||||
{
|
||||
key: "modifiers",
|
||||
label: "修饰键",
|
||||
type: "checkbox-group",
|
||||
options: [
|
||||
{ label: "Ctrl", value: "ctrl" },
|
||||
@ -166,6 +182,8 @@ export default defineComponent({
|
||||
{ label: "Alt", value: "alt" },
|
||||
{ label: "Meta", value: "meta" },
|
||||
],
|
||||
defaultValue: [],
|
||||
width: 7,
|
||||
},
|
||||
],
|
||||
paste: [
|
||||
@ -202,7 +220,7 @@ export default defineComponent({
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.y",
|
||||
@ -210,7 +228,7 @@ export default defineComponent({
|
||||
icon: "drag_handle",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.width",
|
||||
@ -218,7 +236,7 @@ export default defineComponent({
|
||||
icon: "width",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
width: 3,
|
||||
},
|
||||
{
|
||||
key: "rect.height",
|
||||
@ -226,7 +244,7 @@ export default defineComponent({
|
||||
icon: "height",
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
width: 3,
|
||||
},
|
||||
{ key: "savePath", label: "保存路径", icon: "save", type: "input" },
|
||||
],
|
||||
@ -240,12 +258,14 @@ export default defineComponent({
|
||||
{ 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" },
|
||||
],
|
||||
@ -325,7 +345,7 @@ export default defineComponent({
|
||||
icon: "upload_file",
|
||||
type: "input",
|
||||
},
|
||||
{ key: "files", label: "文件列表", type: "file-list" },
|
||||
{ key: "files", label: "文件列表", type: "file-list", width: 12 },
|
||||
],
|
||||
value: [
|
||||
{
|
||||
@ -333,8 +353,15 @@ export default defineComponent({
|
||||
label: "元素选择器",
|
||||
icon: "input",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
{
|
||||
key: "value",
|
||||
label: "设置的值",
|
||||
icon: "edit",
|
||||
type: "input",
|
||||
width: 6,
|
||||
},
|
||||
{ key: "value", label: "设置的值", icon: "edit", type: "input" },
|
||||
],
|
||||
check: [
|
||||
{
|
||||
@ -342,8 +369,15 @@ export default defineComponent({
|
||||
label: "复选框/选框选择器",
|
||||
icon: "check_box",
|
||||
type: "input",
|
||||
width: 8,
|
||||
},
|
||||
{
|
||||
key: "checked",
|
||||
label: "选中状态",
|
||||
type: "checkbox",
|
||||
defaultValue: false,
|
||||
width: 4,
|
||||
},
|
||||
{ key: "checked", label: "选中状态", type: "checkbox" },
|
||||
],
|
||||
focus: [
|
||||
{
|
||||
@ -355,10 +389,23 @@ export default defineComponent({
|
||||
],
|
||||
scroll: [
|
||||
{
|
||||
key: "target",
|
||||
label: "目标元素选择器(可选)",
|
||||
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",
|
||||
@ -367,6 +414,8 @@ export default defineComponent({
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
showWhen: "type",
|
||||
showValue: "position",
|
||||
},
|
||||
{
|
||||
key: "y",
|
||||
@ -375,15 +424,84 @@ export default defineComponent({
|
||||
type: "input",
|
||||
inputType: "number",
|
||||
width: 6,
|
||||
showWhen: "type",
|
||||
showValue: "position",
|
||||
},
|
||||
],
|
||||
download: [
|
||||
{ key: "url", label: "下载URL", icon: "link", type: "input" },
|
||||
{ key: "savePath", label: "保存路径", icon: "save", type: "input" },
|
||||
{
|
||||
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
|
||||
);
|
||||
if (index === -1) {
|
||||
// 添加操作
|
||||
this.$emit("update:selectedActions", [
|
||||
...this.selectedActions,
|
||||
{
|
||||
...action,
|
||||
id: Date.now(),
|
||||
argv: "",
|
||||
saveOutput: false,
|
||||
useOutput: null,
|
||||
cmd: action.value || action.cmd,
|
||||
value: action.value || action.cmd,
|
||||
},
|
||||
]);
|
||||
|
||||
// 初始化配置对象
|
||||
const config = this.getOperationConfig(action.value);
|
||||
if (config) {
|
||||
const newConfigs = { ...this.configs };
|
||||
if (!newConfigs[action.value]) {
|
||||
newConfigs[action.value] = {};
|
||||
}
|
||||
// 设置默认值
|
||||
config.forEach((field) => {
|
||||
if (field.defaultValue !== undefined) {
|
||||
newConfigs[action.value][field.key] = field.defaultValue;
|
||||
}
|
||||
});
|
||||
this.$emit("update:configs", newConfigs);
|
||||
}
|
||||
} else {
|
||||
// 移除操作
|
||||
const newActions = [...this.selectedActions];
|
||||
newActions.splice(index, 1);
|
||||
this.$emit("update:selectedActions", newActions);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -410,12 +528,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.operation-item:hover {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.body--dark .operation-item {
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: rgba(0, 0, 0, 0.15);
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.body--dark .operation-item:hover {
|
||||
@ -452,4 +565,51 @@ export default defineComponent({
|
||||
.operation-item:hover .q-item-section {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.action-card {
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||
/* min-height: 42px; */
|
||||
}
|
||||
|
||||
.action-card:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
background: var(--q-primary-opacity-5);
|
||||
}
|
||||
|
||||
.action-selected {
|
||||
border-color: var(--q-primary);
|
||||
background: var(--q-primary-opacity-10);
|
||||
}
|
||||
|
||||
.body--dark .action-selected {
|
||||
background: var(--q-primary-opacity-40);
|
||||
}
|
||||
|
||||
.body--dark .action-card {
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.body--dark .action-card:hover {
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
|
||||
background: var(--q-primary-opacity-20);
|
||||
}
|
||||
|
||||
.text-caption {
|
||||
font-size: 11px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.q-card__section {
|
||||
padding: 4px !important;
|
||||
}
|
||||
|
||||
.row.q-col-gutter-xs {
|
||||
margin: -2px;
|
||||
}
|
||||
|
||||
.row.q-col-gutter-xs > * {
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,201 +1,248 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<!-- 基础设置 -->
|
||||
<!-- 窗口显示控制 -->
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.show"
|
||||
@update:modelValue="updateConfig('run.show', $event)"
|
||||
label="显示窗口"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 窗口大小 -->
|
||||
<div class="col-12">
|
||||
<div class="text-subtitle2 q-mb-sm">窗口大小</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<UBrowserInput
|
||||
:value="configs.run.width"
|
||||
@update:modelValue="updateConfig('run.width', $event)"
|
||||
type="number"
|
||||
label="宽度"
|
||||
:width="6"
|
||||
icon="width"
|
||||
<div class="row items-center q-gutter-x-md">
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.show"
|
||||
label="显示窗口"
|
||||
@update:model-value="updateConfig('show', $event)"
|
||||
/>
|
||||
<UBrowserInput
|
||||
:value="configs.run.height"
|
||||
@update:modelValue="updateConfig('run.height', $event)"
|
||||
type="number"
|
||||
label="高度"
|
||||
:width="6"
|
||||
icon="height"
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.center"
|
||||
label="居中显示"
|
||||
@update:model-value="updateConfig('center', $event)"
|
||||
/>
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.alwaysOnTop"
|
||||
label="总在最前"
|
||||
@update:model-value="updateConfig('alwaysOnTop', $event)"
|
||||
/>
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.fullscreen"
|
||||
label="全屏显示"
|
||||
@update:model-value="updateConfig('fullscreen', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 窗口位置 -->
|
||||
<!-- 窗口尺寸和位置 -->
|
||||
<div class="col-12">
|
||||
<div class="text-subtitle2 q-mb-sm">窗口位置</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<UBrowserInput
|
||||
:value="configs.run.x"
|
||||
@update:modelValue="updateConfig('run.x', $event)"
|
||||
type="number"
|
||||
label="X坐标"
|
||||
:width="6"
|
||||
icon="drag_handle"
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
v-model.number="localConfigs.run.width"
|
||||
type="number"
|
||||
label="窗口宽度"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfig('width', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
v-model.number="localConfigs.run.height"
|
||||
type="number"
|
||||
label="窗口高度"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfig('height', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
v-model.number="localConfigs.run.x"
|
||||
type="number"
|
||||
label="X坐标"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfig('x', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
v-model.number="localConfigs.run.y"
|
||||
type="number"
|
||||
label="Y坐标"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfig('y', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最大最小尺寸 -->
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
v-model.number="localConfigs.run.minWidth"
|
||||
type="number"
|
||||
label="最小宽度"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfig('minWidth', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
v-model.number="localConfigs.run.minHeight"
|
||||
type="number"
|
||||
label="最小高度"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfig('minHeight', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
v-model.number="localConfigs.run.maxWidth"
|
||||
type="number"
|
||||
label="最大宽度"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfig('maxWidth', $event)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<q-input
|
||||
v-model.number="localConfigs.run.maxHeight"
|
||||
type="number"
|
||||
label="最大高度"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfig('maxHeight', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 窗口行为控制 -->
|
||||
<div class="col-12">
|
||||
<div class="row items-center q-gutter-x-md">
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.resizable"
|
||||
label="可调整大小"
|
||||
@update:model-value="updateConfig('resizable', $event)"
|
||||
/>
|
||||
<UBrowserInput
|
||||
:value="configs.run.y"
|
||||
@update:modelValue="updateConfig('run.y', $event)"
|
||||
type="number"
|
||||
label="Y坐标"
|
||||
:width="6"
|
||||
icon="drag_handle"
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.movable"
|
||||
label="可移动"
|
||||
@update:model-value="updateConfig('movable', $event)"
|
||||
/>
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.minimizable"
|
||||
label="可最小化"
|
||||
@update:model-value="updateConfig('minimizable', $event)"
|
||||
/>
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.maximizable"
|
||||
label="可最大化"
|
||||
@update:model-value="updateConfig('maximizable', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 窗口限制 -->
|
||||
<!-- 特殊功能控制 -->
|
||||
<div class="col-12">
|
||||
<div class="text-subtitle2 q-mb-sm">窗口限制</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<UBrowserInput
|
||||
:value="configs.run.minWidth"
|
||||
@update:modelValue="updateConfig('run.minWidth', $event)"
|
||||
type="number"
|
||||
label="最小宽度"
|
||||
:width="6"
|
||||
icon="width"
|
||||
<div class="row items-center q-gutter-x-md">
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.enableLargerThanScreen"
|
||||
label="允许超出屏幕"
|
||||
@update:model-value="updateConfig('enableLargerThanScreen', $event)"
|
||||
/>
|
||||
<UBrowserInput
|
||||
:value="configs.run.minHeight"
|
||||
@update:modelValue="updateConfig('run.minHeight', $event)"
|
||||
type="number"
|
||||
label="最小高度"
|
||||
:width="6"
|
||||
icon="height"
|
||||
/>
|
||||
<UBrowserInput
|
||||
:value="configs.run.maxWidth"
|
||||
@update:modelValue="updateConfig('run.maxWidth', $event)"
|
||||
type="number"
|
||||
label="最大宽度"
|
||||
:width="6"
|
||||
icon="width"
|
||||
/>
|
||||
<UBrowserInput
|
||||
:value="configs.run.maxHeight"
|
||||
@update:modelValue="updateConfig('run.maxHeight', $event)"
|
||||
type="number"
|
||||
label="最大高度"
|
||||
:width="6"
|
||||
icon="height"
|
||||
<q-checkbox
|
||||
:model-value="localConfigs.run.fullscreenable"
|
||||
label="允许全屏"
|
||||
@update:model-value="updateConfig('fullscreenable', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 窗口行为 -->
|
||||
<!-- 透明度控制 -->
|
||||
<div class="col-12">
|
||||
<div class="text-subtitle2 q-mb-sm">窗口行为</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.center"
|
||||
@update:modelValue="updateConfig('run.center', $event)"
|
||||
label="居中显示"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.resizable"
|
||||
@update:modelValue="updateConfig('run.resizable', $event)"
|
||||
label="允许调整大小"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.movable"
|
||||
@update:modelValue="updateConfig('run.movable', $event)"
|
||||
label="允许移动"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.minimizable"
|
||||
@update:modelValue="updateConfig('run.minimizable', $event)"
|
||||
label="允许最小化"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.maximizable"
|
||||
@update:modelValue="updateConfig('run.maximizable', $event)"
|
||||
label="允许最大化"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.alwaysOnTop"
|
||||
@update:modelValue="updateConfig('run.alwaysOnTop', $event)"
|
||||
label="总是置顶"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.fullscreen"
|
||||
@update:modelValue="updateConfig('run.fullscreen', $event)"
|
||||
label="全屏显示"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.fullscreenable"
|
||||
@update:modelValue="updateConfig('run.fullscreenable', $event)"
|
||||
label="允许全屏"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="configs.run.enableLargerThanScreen"
|
||||
@update:modelValue="updateConfig('run.enableLargerThanScreen', $event)"
|
||||
label="允许超出屏幕大小"
|
||||
/>
|
||||
</div>
|
||||
<div class="row items-center">
|
||||
<div class="q-mr-md">透明度</div>
|
||||
<q-slider
|
||||
class="col"
|
||||
v-model="localConfigs.run.opacity"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:step="0.1"
|
||||
label
|
||||
label-always
|
||||
color="primary"
|
||||
@update:model-value="updateConfig('opacity', $event)"
|
||||
>
|
||||
<template v-slot:thumb-label>
|
||||
{{ localConfigs.run.opacity.toFixed(1) }}
|
||||
</template>
|
||||
</q-slider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue';
|
||||
import UBrowserInput from './operations/UBrowserInput.vue';
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'UBrowserRun',
|
||||
components: {
|
||||
UBrowserInput
|
||||
},
|
||||
name: "UBrowserRun",
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 初始化本地配置
|
||||
this.localConfigs = JSON.parse(JSON.stringify(this.configs));
|
||||
},
|
||||
emits: ['update:configs'],
|
||||
methods: {
|
||||
updateConfig(path, value) {
|
||||
const newConfigs = { ...this.configs };
|
||||
const keys = path.split('.');
|
||||
let current = newConfigs;
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
current[keys[i]] = { ...current[keys[i]] };
|
||||
current = current[keys[i]];
|
||||
}
|
||||
|
||||
current[keys[keys.length - 1]] = value;
|
||||
this.$emit('update:configs', newConfigs);
|
||||
}
|
||||
}
|
||||
updateConfig(key, value) {
|
||||
this.localConfigs.run[key] = value;
|
||||
this.$emit(
|
||||
"update:configs",
|
||||
JSON.parse(JSON.stringify(this.localConfigs))
|
||||
);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
configs: {
|
||||
deep: true,
|
||||
handler(newConfigs) {
|
||||
this.localConfigs = JSON.parse(JSON.stringify(newConfigs));
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -1,189 +1,258 @@
|
||||
<template>
|
||||
<div class="operation-item-argv q-mb-md">
|
||||
<div class="text-subtitle2 q-mb-sm">{{ title }}</div>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<template v-for="(field, index) in fields" :key="index">
|
||||
<!-- 输入框 -->
|
||||
<template v-if="field.type === 'input'">
|
||||
<UBrowserInput
|
||||
:value="getFieldValue(field.key)"
|
||||
@update:modelValue="updateFieldValue(field.key, $event)"
|
||||
:label="field.label"
|
||||
:icon="field.icon"
|
||||
:type="field.inputType || 'text'"
|
||||
:width="field.width"
|
||||
v-bind="field.props || {}"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 选择框 -->
|
||||
<template v-if="field.type === 'select'">
|
||||
<q-select
|
||||
:value="getFieldValue(field.key)"
|
||||
@update:modelValue="updateFieldValue(field.key, $event)"
|
||||
:options="field.options"
|
||||
:label="field.label"
|
||||
outlined
|
||||
dense
|
||||
class="col-12"
|
||||
v-bind="field.props || {}"
|
||||
/>
|
||||
<div class="row q-col-gutter-sm items-center">
|
||||
<template v-for="field in fields" :key="field.key">
|
||||
<div
|
||||
v-if="!field.showWhen || fieldValue[field.showWhen] === field.showValue"
|
||||
:class="['col', field.width ? `col-${field.width}` : 'col-12']"
|
||||
>
|
||||
<!-- 复选框组 -->
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<!-- 单个复选框 -->
|
||||
<template v-if="field.type === 'checkbox'">
|
||||
<div class="col-12">
|
||||
<q-checkbox
|
||||
:value="getFieldValue(field.key)"
|
||||
@update:modelValue="updateFieldValue(field.key, $event)"
|
||||
:label="field.label"
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<!-- 复选框组 -->
|
||||
<template v-if="field.type === 'checkbox-group'">
|
||||
<q-option-group
|
||||
:value="getFieldValue(field.key)"
|
||||
@update:modelValue="updateFieldValue(field.key, $event)"
|
||||
:options="field.options"
|
||||
type="checkbox"
|
||||
inline
|
||||
class="col-12"
|
||||
/>
|
||||
<!-- 文本输入 -->
|
||||
<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>
|
||||
|
||||
<!-- 文本域 -->
|
||||
<template v-if="field.type === 'textarea'">
|
||||
<UBrowserInput
|
||||
:value="getFieldValue(field.key)"
|
||||
@update:modelValue="updateFieldValue(field.key, $event)"
|
||||
<!-- 文本区域 -->
|
||||
<template v-else-if="field.type === 'textarea'">
|
||||
<q-input
|
||||
:model-value="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
:icon="field.icon"
|
||||
type="textarea"
|
||||
dense
|
||||
outlined
|
||||
autogrow
|
||||
class="col-12"
|
||||
/>
|
||||
@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]"
|
||||
:label="field.label"
|
||||
: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-if="field.type === 'cookie-list'">
|
||||
<div class="col-12">
|
||||
<template v-else-if="field.type === 'cookie-list'">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div
|
||||
v-for="(item, idx) in getFieldValue(field.key)"
|
||||
:key="idx"
|
||||
class="row q-col-gutter-sm q-mb-sm"
|
||||
v-for="(cookie, index) in fieldValue[field.key] || [{}]"
|
||||
:key="index"
|
||||
class="col-12"
|
||||
>
|
||||
<UBrowserInput
|
||||
:value="item.name"
|
||||
@update:modelValue="
|
||||
updateCookieField(field.key, idx, 'name', $event)
|
||||
"
|
||||
label="名称"
|
||||
:width="5"
|
||||
icon="label"
|
||||
/>
|
||||
<UBrowserInput
|
||||
:value="item.value"
|
||||
@update:modelValue="
|
||||
updateCookieField(field.key, idx, 'value', $event)
|
||||
"
|
||||
label="值"
|
||||
:width="5"
|
||||
icon="edit"
|
||||
/>
|
||||
<div class="col-2 flex items-center">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
dense
|
||||
color="negative"
|
||||
icon="remove"
|
||||
@click="removeCookie(field.key, idx)"
|
||||
v-if="getFieldValue(field.key).length > 1"
|
||||
/>
|
||||
<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>
|
||||
<q-btn
|
||||
outline
|
||||
color="primary"
|
||||
label="添加Cookie"
|
||||
icon="add"
|
||||
@click="addCookie(field.key)"
|
||||
class="q-mt-sm"
|
||||
/>
|
||||
</div>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
color="primary"
|
||||
icon="add"
|
||||
label="添加Cookie"
|
||||
@click="addCookie(field.key)"
|
||||
class="q-mt-xs"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 参数列表 -->
|
||||
<template v-if="field.type === 'param-list'">
|
||||
<div class="col-12">
|
||||
<div class="row items-center q-gutter-sm q-mb-sm">
|
||||
<UBrowserInput
|
||||
v-model="newParam"
|
||||
label="参数"
|
||||
:width="10"
|
||||
icon="functions"
|
||||
<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])
|
||||
"
|
||||
/>
|
||||
<q-btn flat round dense icon="add" @click="addParam(field.key)" />
|
||||
</div>
|
||||
<div v-if="getFieldValue(field.key).length > 0" class="q-mt-sm">
|
||||
<q-chip
|
||||
v-for="(param, idx) in getFieldValue(field.key)"
|
||||
:key="idx"
|
||||
removable
|
||||
@remove="removeParam(field.key, idx)"
|
||||
>
|
||||
{{ param }}
|
||||
</q-chip>
|
||||
<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>
|
||||
|
||||
<!-- 文件列表 -->
|
||||
<template v-if="field.type === 'file-list'">
|
||||
<div class="col-12">
|
||||
<div class="row items-center q-gutter-sm q-mb-sm">
|
||||
<UBrowserInput
|
||||
v-model="newFile"
|
||||
label="文件路径"
|
||||
:width="10"
|
||||
icon="upload_file"
|
||||
/>
|
||||
<q-btn flat round dense icon="add" @click="addFile(field.key)" />
|
||||
</div>
|
||||
<div v-if="getFieldValue(field.key).length > 0" class="q-mt-sm">
|
||||
<q-chip
|
||||
v-for="(file, idx) in getFieldValue(field.key)"
|
||||
:key="idx"
|
||||
removable
|
||||
@remove="removeFile(field.key, idx)"
|
||||
>
|
||||
{{ file }}
|
||||
</q-chip>
|
||||
<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"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 按钮组 -->
|
||||
<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>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import UBrowserInput from "./UBrowserInput.vue";
|
||||
import { get, set } from "lodash";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserOperation",
|
||||
components: {
|
||||
UBrowserInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
newParam: "",
|
||||
newFile: "",
|
||||
};
|
||||
},
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
@ -193,78 +262,125 @@ export default defineComponent({
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:configs"],
|
||||
data() {
|
||||
return {
|
||||
fieldValue: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// 初始化字段值,确保有默认值
|
||||
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 {
|
||||
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);
|
||||
},
|
||||
|
||||
// Cookie列表相关方法
|
||||
addCookie(key) {
|
||||
const items = [...(this.getFieldValue(key) || [])];
|
||||
items.push({ name: "", value: "" });
|
||||
this.updateFieldValue(key, items);
|
||||
if (!this.fieldValue[key]) {
|
||||
this.fieldValue[key] = [];
|
||||
}
|
||||
this.fieldValue[key].push({ name: "", value: "" });
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
removeCookie(key, index) {
|
||||
const items = [...this.getFieldValue(key)];
|
||||
items.splice(index, 1);
|
||||
this.updateFieldValue(key, items);
|
||||
},
|
||||
updateCookieField(key, index, field, value) {
|
||||
const items = [...this.getFieldValue(key)];
|
||||
items[index] = {
|
||||
...items[index],
|
||||
[field]: value,
|
||||
};
|
||||
this.updateFieldValue(key, items);
|
||||
},
|
||||
addParam(key) {
|
||||
if (this.newParam) {
|
||||
const params = [...(this.getFieldValue(key) || [])];
|
||||
params.push(this.newParam);
|
||||
this.updateFieldValue(key, params);
|
||||
this.newParam = "";
|
||||
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) {
|
||||
const params = [...this.getFieldValue(key)];
|
||||
params.splice(index, 1);
|
||||
this.updateFieldValue(key, params);
|
||||
this.fieldValue[key].splice(index, 1);
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
|
||||
// 文件列表相关方法
|
||||
addFile(key) {
|
||||
if (this.newFile) {
|
||||
const files = [...(this.getFieldValue(key) || [])];
|
||||
files.push(this.newFile);
|
||||
this.updateFieldValue(key, files);
|
||||
this.newFile = "";
|
||||
if (!this.fieldValue[key]) {
|
||||
this.fieldValue[key] = [];
|
||||
}
|
||||
this.fieldValue[key].push("");
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
removeFile(key, index) {
|
||||
const files = [...this.getFieldValue(key)];
|
||||
files.splice(index, 1);
|
||||
this.updateFieldValue(key, files);
|
||||
this.fieldValue[key].splice(index, 1);
|
||||
this.updateValue(key, this.fieldValue[key]);
|
||||
},
|
||||
getFieldValue(key) {
|
||||
return this.configs[this.action][key];
|
||||
},
|
||||
updateFieldValue(key, value) {
|
||||
this.$emit("update:configs", {
|
||||
...this.configs,
|
||||
[this.action]: {
|
||||
...this.configs[this.action],
|
||||
[key]: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
// 监听配置变化
|
||||
configs: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.fields.forEach((field) => {
|
||||
const value = get(this.configs[this.action], field.key);
|
||||
if (value !== undefined) {
|
||||
this.fieldValue[field.key] = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.operation-item-argv {
|
||||
padding: 0 4px;
|
||||
.button-group-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
flex: 1;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-btn) {
|
||||
min-height: 24px;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user