新增模拟鼠标操作

This commit is contained in:
fofolee 2025-01-02 18:25:35 +08:00
parent 64b6a2a6d9
commit 9e213f7b65
11 changed files with 243 additions and 209 deletions

View File

@ -51,14 +51,14 @@
:is="command.component"
v-model="argvLocal"
:command="command"
class="col"
class="col q-mt-sm"
v-bind="command.componentProps || {}"
/>
<MultiParamInput
<MultiParams
v-else
v-model="argvLocal"
:command="command"
class="col"
:class="command.config?.length ? 'col q-mt-sm' : 'col'"
/>
</div>
</div>
@ -71,7 +71,7 @@
import { defineComponent, inject } from "vue";
import { validateVariableName } from "js/common/variableValidator";
import VariableInput from "./ui/VariableInput.vue";
import MultiParamInput from "./ui/MultiParamInput.vue";
import MultiParams from "./ui/MultiParams.vue";
import CommandHead from "./card/CommandHead.vue";
import * as CardComponents from "js/composer/cardComponents";
@ -79,7 +79,7 @@ export default defineComponent({
name: "ComposerCard",
components: {
VariableInput,
MultiParamInput,
MultiParams,
CommandHead,
...CardComponents,
},
@ -362,7 +362,6 @@ export default defineComponent({
display: grid;
grid-template-rows: 1fr;
transition: grid-template-rows 0.2s cubic-bezier(0.4, 0, 0.2, 1);
margin-top: 8px;
}
.command-content {

View File

@ -5,6 +5,8 @@
:icon="isAllCollapsed ? 'unfold_more' : 'unfold_less'"
dense
flat
rounded
size="9px"
@click="$emit('action', isAllCollapsed ? 'expandAll' : 'collapseAll')"
>
<q-tooltip>{{ isAllCollapsed ? "展开所有" : "折叠所有" }}</q-tooltip>

View File

@ -1,101 +0,0 @@
<template>
<div class="row q-col-gutter-sm">
<div class="col">
<VariableInput
:model-value="inputValue"
:label="inputLabel"
:command="command"
@update:model-value="handleInputChange"
/>
</div>
<div class="col-4">
<q-select
v-model="selectedFunction"
:options="options"
:label="selectLabel"
dense
filled
emit-value
map-options
@update:model-value="handleFunctionChange"
>
<template v-slot:prepend>
<q-icon :name="command.icon || 'functions'" />
</template>
</q-select>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import VariableInput from "components/composer/ui/VariableInput.vue";
export default defineComponent({
name: "FunctionSelector",
components: {
VariableInput,
},
props: {
modelValue: {
type: String,
default: "",
},
command: {
type: Object,
required: true,
},
options: {
type: Array,
required: true,
},
inputLabel: {
type: String,
default: "输入值",
},
selectLabel: {
type: String,
default: "选择函数",
},
},
emits: ["update:model-value"],
data() {
return {
selectedFunction: this.options[0]?.value || "",
inputValue: "",
};
},
watch: {
modelValue: {
immediate: true,
handler(val) {
if (!val) {
this.selectedFunction = this.options[0]?.value || "";
this.inputValue = "";
return;
}
//
const match = val.match(/(.+?)\((.*)\)/);
if (match) {
this.selectedFunction = match[1];
this.inputValue = match[2];
}
},
},
},
methods: {
generateCode() {
if (!this.selectedFunction || !this.inputValue) return "";
return `${this.selectedFunction}(${this.inputValue})`;
},
handleInputChange(value) {
this.inputValue = value;
this.$emit("update:model-value", this.generateCode());
},
handleFunctionChange(value) {
this.selectedFunction = value;
this.$emit("update:model-value", this.generateCode());
},
},
});
</script>

View File

@ -1,77 +0,0 @@
<template>
<div class="multi-param-input row q-col-gutter-sm">
<div
v-for="item in config"
:key="item.key"
:class="['param-item', `col-${item.width || 12}`]"
>
<VariableInput
v-model="item.value"
:label="item.label"
:command="item"
@update:model-value="handleArgvChange(item.key, $event)"
/>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import VariableInput from "./VariableInput.vue";
export default defineComponent({
name: "MultiParamInput",
components: {
VariableInput,
},
props: {
modelValue: {
type: String,
default: "",
},
command: {
type: Object,
required: true,
},
},
emits: ["update:modelValue"],
computed: {
config() {
return this.command.config || [];
},
},
methods: {
handleArgvChange(key, value) {
//
const args = this.config.reduce((acc, item) => {
acc[item.key] = item.key === key ? value : item.value ?? "";
return acc;
}, {});
//
const argv = this.config
.map((item) => args[item.key])
.filter((val) => val !== undefined && val !== "")
.join(",");
this.$emit("update:modelValue", `${this.command.value}(${argv})`);
},
},
});
</script>
<style scoped>
.multi-param-input {
width: 100%;
}
.param-item {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.param-item :deep(.q-field) {
margin-bottom: 0;
}
</style>

View File

@ -0,0 +1,132 @@
<template>
<div class="flex-container">
<div
v-if="hasFunctionSelector"
class="flex-item"
:style="{ flex: command.functionSelector.width || 3 }"
>
<q-select
v-model="selectedFunction"
:options="command.functionSelector.options"
:label="command.functionSelector.selectLabel"
dense
filled
emit-value
map-options
@update:model-value="handleFunctionChange"
>
<template v-slot:prepend>
<q-icon :name="command.icon || 'functions'" />
</template>
</q-select>
</div>
<div
v-for="item in config"
:key="item.key"
class="flex-item"
:style="{ flex: item.width || 12 }"
>
<VariableInput
v-model="item.value"
:label="item.label"
:command="item"
@update:model-value="handleArgvChange(item.key, $event)"
/>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import VariableInput from "./VariableInput.vue";
export default defineComponent({
name: "MultiParams",
components: {
VariableInput,
},
props: {
modelValue: {
type: String,
default: "",
},
command: {
type: Object,
required: true,
},
},
emits: ["update:modelValue"],
data() {
return {
selectedFunction: this.command.functionSelector?.options[0]?.value || "",
localConfig: (this.command.config || []).map((item) => ({
...item,
value: item.defaultValue ?? "",
})),
};
},
computed: {
config() {
return this.localConfig;
},
hasFunctionSelector() {
return !!this.command.functionSelector;
},
},
methods: {
generateCode() {
const functionName = this.hasFunctionSelector
? this.selectedFunction
: this.command.value;
const args = this.config
.map((item) => item.value)
.filter((val) => val !== undefined && val !== "")
.join(",");
return `${functionName}(${args})`;
},
handleArgvChange(key, value) {
const item = this.localConfig.find((item) => item.key === key);
if (item) {
item.value = value;
}
this.$emit("update:modelValue", this.generateCode());
},
handleFunctionChange(value) {
this.selectedFunction = value;
this.$emit("update:modelValue", this.generateCode());
},
},
mounted() {
if (this.command.allowEmptyArgv) {
this.$emit("update:modelValue", this.generateCode());
} else {
const hasDefaultValues = this.localConfig.some(
(item) => item.defaultValue !== undefined && item.defaultValue !== ""
);
if (hasDefaultValues) {
this.$emit("update:modelValue", this.generateCode());
}
}
},
});
</script>
<style scoped>
.flex-container {
display: flex;
flex-wrap: wrap;
gap: 8px;
width: 100%;
}
.flex-item {
min-width: 100px; /* 设置最小宽度以确保内容可读性 */
}
@media (max-width: 600px) {
.flex-item {
flex: 1 1 100% !important; /* 在小屏幕上强制换行 */
}
}
</style>

View File

@ -4,9 +4,6 @@ import { defineAsyncComponent } from "vue";
export const KeyEditor = defineAsyncComponent(() =>
import("components/composer/ui/KeyEditor.vue")
);
export const FunctionSelector = defineAsyncComponent(() =>
import("components/composer/ui/FunctionSelector.vue")
);
// Control Flow Components
export const ConditionalJudgment = defineAsyncComponent(() =>

View File

@ -4,7 +4,7 @@ import { systemCommands } from "./systemCommands";
import { notifyCommands } from "./notifyCommands";
import { textProcessingCommands } from "./textProcessingCommands";
import { otherCommands } from "./otherCommands";
import { keyCommands } from "./keyCommands";
import { simulateCommands } from "./simulateCommands";
import { controlCommands } from "./controlCommands";
export const commandCategories = [
@ -15,5 +15,5 @@ export const commandCategories = [
textProcessingCommands,
controlCommands,
otherCommands,
keyCommands,
simulateCommands,
];

View File

@ -1,13 +0,0 @@
export const keyCommands = {
label: "按键操作",
icon: "keyboard",
defaultOpened: false,
commands: [
{
value: "keyTap",
label: "模拟按键",
config: [],
component: "KeyEditor",
},
],
};

View File

@ -0,0 +1,81 @@
export const simulateCommands = {
label: "模拟操作",
icon: "keyboard",
defaultOpened: false,
commands: [
{
value: "keyTap",
label: "模拟按键",
config: [],
component: "KeyEditor",
},
{
value: "utools",
label: "鼠标点击",
allowEmptyArgv: true,
config: [
{
label: "X坐标留空则原地点击",
icon: "drag_handle",
type: "input",
inputType: "number",
width: 8,
},
{
label: "Y坐标留空则原地点击",
icon: "drag_handle",
type: "input",
inputType: "number",
width: 8,
},
],
functionSelector: {
selectLabel: "鼠标动作",
options: [
{
label: "单击",
value: "utools.simulateMouseClick",
},
{
label: "右击",
value: "utools.simulateMouseRightClick",
},
{
label: "双击",
value: "utools.simulateMouseDoubleClick",
},
],
width: 2,
allowEmptyArgv: true,
},
},
{
value: "utools.simulateMouseMove",
label: "鼠标移动",
config: [
{
label: "X坐标",
icon: "drag_handle",
defaultValue: 0,
type: "input",
inputType: "number",
width: 8,
},
{
label: "Y坐标",
icon: "drag_handle",
defaultValue: 0,
type: "input",
inputType: "number",
width: 8,
},
],
},
{
value: "utools.getCursorScreenPoint",
label: "获取鼠标坐标",
config: [],
allowEmptyArgv: true,
},
],
};

View File

@ -33,7 +33,7 @@ export const systemCommands = {
value: "electron.clipboard.readText",
label: "获取剪贴板内容",
config: [],
icon: "content_copy",
allowEmptyArgv: true,
},
],
};

View File

@ -8,9 +8,15 @@ export const textProcessingCommands = {
label: "编解码",
desc: "文本编解码",
icon: "code",
component: "FunctionSelector",
componentProps: {
inputLabel: "要编解码的文本",
config: [
{
label: "要编解码的文本",
icon: "text_fields",
type: "input",
width: 8,
},
],
functionSelector: {
selectLabel: "编解码方式",
options: [
{
@ -40,6 +46,7 @@ export const textProcessingCommands = {
value: "quickcomposer.textProcessing.htmlDecode",
},
],
width: 3,
},
},
{
@ -57,9 +64,15 @@ export const textProcessingCommands = {
label: "哈希计算",
desc: "计算文本的哈希值",
icon: "enhanced_encryption",
component: "FunctionSelector",
componentProps: {
inputLabel: "要计算哈希的文本",
config: [
{
label: "要计算哈希的文本",
icon: "text_fields",
type: "input",
width: 8,
},
],
functionSelector: {
selectLabel: "哈希算法",
options: [
{ label: "MD5", value: "quickcomposer.textProcessing.md5Hash" },
@ -69,6 +82,7 @@ export const textProcessingCommands = {
{ label: "SM3", value: "quickcomposer.textProcessing.sm3Hash" },
],
},
width: 3,
},
{
value: "quickcomposer.textProcessing.reverseString",