编排补齐utools窗口交互和内容输入的功能

This commit is contained in:
fofolee 2025-01-09 17:55:50 +08:00
parent 036b6fa934
commit 682f6d0bcd
7 changed files with 338 additions and 102 deletions

View File

@ -1,12 +1,10 @@
const { findImage } = require("./imageFinder");
const { captureScreen } = require("./screenCapture");
const sendText = require("./sendText");
const { keyboardTap, keySequence } = require("./keyboardTap");
module.exports = {
findImage,
captureScreen,
sendText,
keyboardTap,
keySequence,
};

View File

@ -1,11 +0,0 @@
const sendText = (text, simulateCopy = false) => {
if (simulateCopy) {
window.utools.copyText(text);
quickcommand.sleep(200);
quickcommand.simulatePaste();
} else {
window.utools.hideMainWindowTypeString(text);
}
};
module.exports = sendText;

View File

@ -7,15 +7,30 @@
>
<div class="dict-editor">
<div
v-for="(item, index) in items"
:key="index"
v-for="(item, index) in allItems"
:key="`${item.type}-${index}`"
class="row q-col-gutter-sm items-center"
>
<div class="col-4">
<q-select
v-if="options?.items"
<template v-if="item.type === 'fixed'">
<q-input
:model-value="item.key"
:options="options.items"
:label="getKeyLabel(item.key)"
dense
filled
readonly
disable
>
<template v-slot:prepend>
<q-icon name="lock" />
</template>
</q-input>
</template>
<template v-else>
<q-select
v-if="options?.optionKeys"
:model-value="item.key"
:options="normalizedOptionKeys"
label="名称"
dense
filled
@ -23,13 +38,22 @@
input-debounce="0"
:hide-selected="!!inputValue"
@filter="filterFn"
@update:model-value="(val) => handleSelect(val, index)"
@input-value="(val) => handleInput(val, index)"
@update:model-value="
(val) => handleSelect(val, getEditableIndex(index))
"
@input-value="(val) => handleInput(val, getEditableIndex(index))"
@blur="handleBlur"
>
<template v-slot:prepend>
<q-icon name="code" />
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section>
{{ getKeyLabel(scope.opt) }}
</q-item-section>
</q-item>
</template>
</q-select>
<q-input
v-else
@ -37,12 +61,15 @@
label="名称"
dense
filled
@update:model-value="(val) => updateItemKey(val, index)"
@update:model-value="
(val) => updateItemKey(val, getEditableIndex(index))
"
>
<template v-slot:prepend>
<q-icon name="code" />
</template>
</q-input>
</template>
</div>
<div class="col">
<VariableInput
@ -50,12 +77,14 @@
label="值"
icon="code"
class="col-grow"
@update:model-value="(val) => updateItemValue(val, index)"
@update:model-value="
(val) => updateItemValue(val, index, item.type === 'fixed')
"
/>
</div>
<div class="col-auto">
<div v-if="item.type !== 'fixed'" class="col-auto">
<div class="btn-container">
<template v-if="items.length === 1">
<template v-if="editableItems.length === 1">
<q-btn
flat
dense
@ -65,14 +94,16 @@
@click="addItem"
/>
</template>
<template v-else-if="index === items.length - 1">
<template
v-else-if="getEditableIndex(index) === editableItems.length - 1"
>
<q-btn
flat
dense
size="sm"
icon="remove"
class="top-btn"
@click="removeItem(index)"
@click="removeItem(getEditableIndex(index))"
/>
<q-btn
flat
@ -90,7 +121,7 @@
size="sm"
icon="remove"
class="center-btn"
@click="removeItem(index)"
@click="removeItem(getEditableIndex(index))"
/>
</template>
</div>
@ -112,7 +143,8 @@ import BorderLabel from "components/composer/common/BorderLabel.vue";
*
* @property {Object} modelValue - 绑定的字典对象
* @property {Object} options - 配置选项
* @property {String[]} [options.items] - 键名的下拉选择选项
* @property {String[]|Object[]} [options.optionKeys] - 可选键名
* @property {String[]|Object[]} [options.fixedKeys] - 固定键名
*
* @example
* //
@ -164,40 +196,105 @@ export default defineComponent({
emits: ["update:modelValue"],
data() {
const modelEntries = Object.entries(this.modelValue || {});
const fixedKeys = this.normalizeKeys(this.options?.fixedKeys || []);
const fixedKeyValues = fixedKeys.map((key) => ({
type: "fixed",
key: key.value,
value:
modelEntries.find(([k]) => k === key.value)?.[1] ||
newVarInputVal("str"),
}));
const editableEntries = modelEntries.filter(
([key]) => !fixedKeys.some((k) => k.value === key)
);
return {
localItems: modelEntries.length
? modelEntries.map(([key, value]) => ({ key, value }))
: [
{
key: "",
value: newVarInputVal("str"),
},
],
filterOptions: this.options?.items || [],
fixedItems: fixedKeyValues,
localItems: editableEntries.length
? editableEntries.map(([key, value]) => ({
type: "editable",
key,
value,
}))
: [{ type: "editable", key: "", value: newVarInputVal("str") }],
filterOptions: this.normalizeKeys(this.options?.optionKeys || []),
inputValue: "",
};
},
computed: {
items: {
editableItems: {
get() {
return this.localItems;
},
set(newItems) {
this.localItems = newItems;
this.updateModelValue();
},
},
allItems() {
return [...this.fixedItems, ...this.localItems];
},
normalizedOptionKeys() {
return this.filterOptions;
},
},
methods: {
normalizeKeys(keys) {
return keys.map((key) => {
if (typeof key === "string") {
return { value: key, label: key };
}
return key;
});
},
getKeyLabel(key) {
if (typeof key === "object") return key.label;
const allKeys = [
...this.normalizeKeys(this.options?.fixedKeys || []),
...this.normalizeKeys(this.options?.optionKeys || []),
];
return allKeys.find((k) => k.value === key)?.label || key;
},
getEditableIndex(index) {
return index - this.fixedItems.length;
},
updateModelValue() {
const dict = {};
newItems.forEach((item) => {
//
this.fixedItems.forEach((item) => {
if (item.key) {
dict[item.key] = item.value;
}
});
//
this.localItems.forEach((item) => {
if (item.key) {
dict[item.key] = item.value;
}
});
this.$emit("update:modelValue", dict);
},
updateItemValue(val, index, isFixed = false) {
if (isFixed) {
const newItems = [...this.fixedItems];
newItems[index].value = val;
this.fixedItems = newItems;
} else {
const newItems = [...this.localItems];
newItems[index].value = val;
this.localItems = newItems;
}
this.updateModelValue();
},
},
methods: {
addItem() {
this.items = [
...this.items,
this.localItems = [
...this.localItems,
{
key: "",
value: newVarInputVal("str"),
@ -205,7 +302,7 @@ export default defineComponent({
];
},
removeItem(index) {
const newItems = [...this.items];
const newItems = [...this.localItems];
newItems.splice(index, 1);
if (newItems.length === 0) {
newItems.push({
@ -213,45 +310,46 @@ export default defineComponent({
value: newVarInputVal("str"),
});
}
this.items = newItems;
this.localItems = newItems;
},
updateItemKey(val, index) {
const newItems = [...this.items];
const newItems = [...this.localItems];
newItems[index].key = val;
this.items = newItems;
},
updateItemValue(val, index) {
const newItems = [...this.items];
newItems[index].value = val;
this.items = newItems;
this.localItems = newItems;
},
handleInput(val, index) {
this.inputValue = val;
if (val && !this.filterOptions.includes(val)) {
const newItems = [...this.items];
if (val) {
const newItems = [...this.localItems];
newItems[index].key = val;
this.items = newItems;
this.localItems = newItems;
this.updateModelValue();
}
},
handleSelect(val, index) {
this.inputValue = "";
const newItems = [...this.items];
newItems[index].key = val;
this.items = newItems;
const newItems = [...this.localItems];
newItems[index].key = val.value || val;
this.localItems = newItems;
this.updateModelValue();
},
handleBlur() {
this.inputValue = "";
},
filterFn(val, update) {
if (!this.options?.items) return;
if (!this.options?.optionKeys) return;
update(() => {
if (val === "") {
this.filterOptions = this.options.items;
this.filterOptions = this.normalizeKeys(this.options.optionKeys);
} else {
const needle = val.toLowerCase();
this.filterOptions = this.options.items.filter(
(v) => v.toLowerCase().indexOf(needle) > -1
this.filterOptions = this.normalizeKeys(
this.options.optionKeys
).filter(
(v) =>
v.label.toLowerCase().indexOf(needle) > -1 ||
v.value.toLowerCase().indexOf(needle) > -1
);
}
});

View File

@ -102,7 +102,7 @@
<DictEditor
v-model="argvs.otherHeaders"
:options="{
items: commonHeaderOptions,
optionKeys: commonHeaderOptions,
}"
@update:model-value="updateHeaders"
/>

View File

@ -33,7 +33,7 @@ export const simulateCommands = {
],
},
{
value: "quickcomposer.simulate.sendText",
value: "utools.hideMainWindowTypeString",
label: "发送文本",
config: [
{
@ -41,18 +41,73 @@ export const simulateCommands = {
label: "要发送的文本内容",
type: "varInput",
icon: "send",
width: 9,
width: 12,
},
],
subCommands: [
{
value: "utools.hideMainWindowTypeString",
label: "模拟输入",
icon: "keyboard",
},
{
label: "发送方式",
type: "select",
defaultValue: false,
icon: "keyboard",
options: [
{ label: "模拟输入", value: false },
{ label: "模拟粘贴", value: true },
value: "utools.hideMainWindowPasteText",
label: "模拟粘贴",
icon: "content_paste",
},
],
},
{
value: "utools.hideMainWindowPasteFile",
label: "模拟粘贴文件/图片",
icon: "file_copy",
subCommands: [
{
value: "utools.hideMainWindowPasteFile",
label: "粘贴文件",
icon: "file_copy",
config: [
{
key: "file",
label: "文件路径",
type: "varInput",
icon: "description",
width: 12,
options: {
dialog: {
type: "open",
options: {
title: "选择文件",
properties: [
"openFile",
"multiSelections",
"showHiddenFiles",
],
},
},
},
},
],
},
{
value: "utools.hideMainWindowPasteImage",
label: "粘贴图片",
icon: "image",
config: [
{
key: "image",
label: "图片路径/base64",
type: "varInput",
icon: "image",
width: 12,
options: {
dialog: {
title: "选择图片",
properties: ["openFile", "showHiddenFiles"],
},
},
},
],
width: 3,
},
],
},

View File

@ -1,3 +1,5 @@
import { newVarInputVal } from "js/composer/varInputValManager";
export const utoolsCommands = {
label: "uTools功能",
icon: "insights",
@ -83,5 +85,95 @@ export const utoolsCommands = {
},
],
},
{
value: "utools.findInPage",
label: "插件内查找",
desc: "插件内查找",
icon: "search",
subCommands: [
{
value: "utools.findInPage",
label: "查找文本",
desc: "查找文本",
icon: "search",
config: [
{
key: "text",
label: "文本",
type: "varInput",
icon: "search",
width: 12,
},
{
key: "options",
label: "选项",
type: "dictEditor",
icon: "settings",
options: {
fixedKeys: [
{
value: "forward",
label: "向前查找",
},
{
value: "findNext",
label: "查找下一个",
},
{
value: "matchCase",
label: "区分大小写",
},
{
value: "wordStart",
label: "单词开头",
},
{
value: "medialCapitalAsWordStart",
label: "中缀大写作为单词开头",
},
],
},
defaultValue: {
forward: newVarInputVal("var", "true"),
findNext: newVarInputVal("var", "false"),
matchCase: newVarInputVal("var", "false"),
wordStart: newVarInputVal("var", "false"),
medialCapitalAsWordStart: newVarInputVal("var", "false"),
},
width: 12,
},
],
},
{
value: "utools.stopFindInPage",
label: "停止查找",
desc: "停止查找",
icon: "stop",
config: [
{
key: "action",
label: "动作",
type: "buttonGroup",
icon: "settings",
width: 12,
options: [
{ label: "清除选择", value: "clearSelection" },
{ label: "保持选择", value: "keepSelection" },
{ label: "激活选择", value: "activateSelection" },
],
defaultValue: "clearSelection",
},
],
},
],
},
{
value: "utools.getWindowType",
label: "获取当前窗口类型",
desc: "获取当前窗口类型",
icon: "window",
outputVariable: "windowType",
saveOutput: true,
},
],
};

View File

@ -424,6 +424,10 @@ interface UToolsApi {
*
*/
startDrag(file: string | string[]): void;
/**
*
*/
getWindowType(): "main" | "detach" | "browser";
/**
*
*/