补全屏幕截图、屏幕取色、获取显示器信息、剪贴板操作

This commit is contained in:
fofolee 2025-01-09 22:43:33 +08:00
parent 682f6d0bcd
commit 378ae7f92f
10 changed files with 420 additions and 83 deletions

View File

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

View File

@ -187,7 +187,7 @@ async function captureLinuxScreen() {
}
// 统一的截图接口
async function captureScreen() {
async function captureFullScreen() {
try {
if (process.platform === "darwin") {
return await captureMacScreen();
@ -202,4 +202,41 @@ async function captureScreen() {
return null;
}
module.exports = { captureScreen };
function captureAreaScreen() {
return new Promise((resolve) => {
window.utools.screenCapture((data) => {
resolve(data);
});
});
}
async function captureScreen(range = "fullscreen") {
return range === "fullscreen"
? await captureFullScreen()
: await captureAreaScreen();
}
async function captureScreenToFile(range = "fullscreen", path = null) {
if (!path) return null;
const result = await captureScreen(range);
if (!result) return null;
fs.writeFileSync(
path,
result.replace("data:image/png;base64,", ""),
"base64"
);
return result;
}
async function captureScreenToClipboard(range = "fullscreen") {
const result = await captureScreen(range);
if (!result) return null;
window.utools.copyImage(result);
return result;
}
module.exports = {
captureScreen,
captureScreenToFile,
captureScreenToClipboard,
};

View File

@ -0,0 +1,11 @@
const screenColorPick = () => {
return new Promise((resolve) => {
utools.screenColorPick((color) => {
resolve(color);
});
});
};
module.exports = {
screenColorPick,
};

View File

@ -41,16 +41,16 @@ export default defineComponent({
// , excludeConfig[]
this.modelValue.config?.filter(
(_, index) =>
!this.getSelectFunction()?.excludeConfig?.includes(index)
!this.getSelectSubCommand()?.excludeConfig?.includes(index)
) || []
);
},
// configconfig
functionConfig() {
return this.getSelectFunction()?.config || [];
subCommandConfig() {
return this.getSelectSubCommand()?.config || [];
},
localConfig() {
return [...this.commonConfig, ...this.functionConfig].map((item) => {
return [...this.commonConfig, ...this.subCommandConfig].map((item) => {
const value =
item.type === "varInput"
? item.defaultValue || newVarInputVal("str")
@ -79,7 +79,7 @@ export default defineComponent({
});
// 使
this.getSelectFunction(value)?.config?.forEach((config, index) => {
this.getSelectSubCommand(value)?.config?.forEach((config, index) => {
newArgvs[this.commonConfig.length + index] = config.defaultValue;
});
@ -96,7 +96,7 @@ export default defineComponent({
},
},
methods: {
getSelectFunction(funcName = this.funcName) {
getSelectSubCommand(funcName = this.funcName) {
return this.modelValue.subCommands?.find(
(item) => item.value === funcName
);
@ -165,7 +165,7 @@ export default defineComponent({
},
getSummary(argvs) {
// header
const funcNameLabel = this.getSelectFunction()?.label;
const funcNameLabel = this.getSelectSubCommand()?.label;
const subFeature = funcNameLabel ? `${funcNameLabel} ` : "";
const allArgvs = argvs
.filter((item) => item != null && item != "")

View File

@ -82,7 +82,10 @@
"
/>
</div>
<div v-if="item.type !== 'fixed'" class="col-auto">
<div
v-if="item.type !== 'fixed' && !options?.disableAdd"
class="col-auto"
>
<div class="btn-container">
<template v-if="editableItems.length === 1">
<q-btn
@ -145,6 +148,7 @@ import BorderLabel from "components/composer/common/BorderLabel.vue";
* @property {Object} options - 配置选项
* @property {String[]|Object[]} [options.optionKeys] - 可选键名
* @property {String[]|Object[]} [options.fixedKeys] - 固定键名
* @property {Boolean} [options.disableAdd] - 禁止添加新的键值对
*
* @example
* //
@ -195,30 +199,7 @@ 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 {
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: "",
};
},
@ -232,11 +213,49 @@ export default defineComponent({
this.updateModelValue();
},
},
normalizedOptionKeys() {
return this.filterOptions;
},
//
normalizedFixedKeys() {
return this.normalizeKeys(this.options?.fixedKeys || []);
},
// modelValue
modelEntries() {
return Object.entries(this.modelValue || {});
},
//
fixedItems() {
return this.normalizedFixedKeys.map((key) => ({
type: "fixed",
key: key.value,
value:
this.modelEntries.find(([k]) => k === key.value)?.[1] ||
newVarInputVal("str"),
}));
},
//
localItems() {
//
const editableEntries = this.modelEntries.filter(
([key]) => !this.normalizedFixedKeys.some((k) => k.value === key)
);
return editableEntries.length || this.options?.disableAdd
? editableEntries.map(([key, value]) => ({
type: "editable",
key,
value,
}))
: [{ type: "editable", key: "", value: newVarInputVal("str") }];
},
//
allItems() {
return [...this.fixedItems, ...this.localItems];
},
normalizedOptionKeys() {
return this.filterOptions;
//
filterOptions() {
return this.normalizeKeys(this.options?.optionKeys || []);
},
},
methods: {
@ -251,10 +270,7 @@ export default defineComponent({
getKeyLabel(key) {
if (typeof key === "object") return key.label;
const allKeys = [
...this.normalizeKeys(this.options?.fixedKeys || []),
...this.normalizeKeys(this.options?.optionKeys || []),
];
const allKeys = [...this.normalizedFixedKeys, ...this.filterOptions];
return allKeys.find((k) => k.value === key)?.label || key;
},
@ -280,62 +296,57 @@ export default defineComponent({
},
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;
const dict = { ...this.modelValue };
const item = this.allItems[index];
if (item.key) {
dict[item.key] = val;
this.$emit("update:modelValue", dict);
}
this.updateModelValue();
},
addItem() {
this.localItems = [
...this.localItems,
{
key: "",
value: newVarInputVal("str"),
},
];
if (this.options?.disableAdd) return;
const dict = { ...this.modelValue };
dict[""] = newVarInputVal("str");
this.$emit("update:modelValue", dict);
},
removeItem(index) {
const newItems = [...this.localItems];
newItems.splice(index, 1);
if (newItems.length === 0) {
newItems.push({
key: "",
value: newVarInputVal("str"),
});
if (this.options?.disableAdd) return;
const dict = { ...this.modelValue };
const item = this.localItems[index];
if (item.key) {
delete dict[item.key];
this.$emit("update:modelValue", dict);
}
this.localItems = newItems;
},
updateItemKey(val, index) {
const newItems = [...this.localItems];
newItems[index].key = val;
this.localItems = newItems;
const dict = { ...this.modelValue };
const oldItem = this.localItems[index];
if (oldItem.key) {
delete dict[oldItem.key];
}
dict[val] = oldItem.value;
this.$emit("update:modelValue", dict);
},
handleInput(val, index) {
this.inputValue = val;
if (val) {
const newItems = [...this.localItems];
newItems[index].key = val;
this.localItems = newItems;
this.updateModelValue();
this.updateItemKey(val, index);
}
},
handleSelect(val, index) {
this.inputValue = "";
const newItems = [...this.localItems];
newItems[index].key = val.value || val;
this.localItems = newItems;
this.updateModelValue();
this.updateItemKey(val.value || val, index);
},
handleBlur() {
this.inputValue = "";
},
filterFn(val, update) {
if (!this.options?.optionKeys) return;

View File

@ -11,6 +11,7 @@ import { codingCommands } from "./codingCommand";
import { mathCommands } from "./mathCommands";
import { userdataCommands } from "./userdataCommands";
import { utoolsCommands } from "./utoolsCommand";
import { screenCommands } from "./screenCommands";
export const commandCategories = [
fileCommands,
@ -25,5 +26,6 @@ export const commandCategories = [
simulateCommands,
mathCommands,
userdataCommands,
screenCommands,
otherCommands,
];

View File

@ -0,0 +1,126 @@
import { newVarInputVal } from "js/composer/varInputValManager";
const XY_DICT_EDITOR = {
label: "坐标",
type: "dictEditor",
icon: "transform",
isCollapse: false,
width: 12,
defaultValue: {
x: newVarInputVal("var", "0"),
y: newVarInputVal("var", "0"),
},
options: {
fixedKeys: [
{ value: "x", label: "X坐标" },
{ value: "y", label: "Y坐标" },
],
disableAdd: true,
},
};
const RECT_DICT_EDITOR = {
label: "区域",
type: "dictEditor",
icon: "transform",
isCollapse: false,
width: 12,
defaultValue: {
x: newVarInputVal("var", "0"),
y: newVarInputVal("var", "0"),
width: newVarInputVal("var", "100"),
height: newVarInputVal("var", "100"),
},
options: {
fixedKeys: [
{ value: "x", label: "X坐标" },
{ value: "y", label: "Y坐标" },
{ value: "width", label: "宽度" },
{ value: "height", label: "高度" },
],
disableAdd: true,
},
};
export const screenCommands = {
label: "显示器",
icon: "screenshot_monitor",
commands: [
{
value: "utools.getPrimaryDisplay",
label: "获取显示器信息",
desc: "获取显示器信息",
icon: "monitor",
outputVariable: "display",
saveOutput: true,
subCommands: [
{
value: "utools.getPrimaryDisplay",
label: "获取主显示器",
icon: "monitor",
},
{
value: "utools.getAllDisplays",
label: "获取所有显示器",
icon: "desktop_windows",
},
{
value: "utools.getDisplayNearestPoint",
label: "获取位置所在显示器",
icon: "gps_fixed",
config: [XY_DICT_EDITOR],
},
{
value: "utools.getDisplayMatching",
label: "获取矩形所在显示器",
icon: "crop_square",
config: [RECT_DICT_EDITOR],
},
],
},
{
value: "utools.screenToDipPoint",
label: "物理/DIP坐标转换",
desc: "屏幕物理坐标和 DIP 坐标转换",
icon: "transform",
outputVariable: "{x,y}",
saveOutput: true,
config: [XY_DICT_EDITOR],
subCommands: [
{
value: "utools.screenToDipPoint",
label: "物理坐标转DIP坐标",
icon: "transform",
},
{
value: "utools.dipToScreenPoint",
label: "DIP坐标转物理坐标",
icon: "transform",
},
],
},
{
value: "utools.screenToDipRect",
label: "物理/DIP区域转换",
desc: "屏幕物理区域和 DIP 区域转换",
icon: "transform",
outputVariable: "{x,y,width,height}",
saveOutput: true,
config: [RECT_DICT_EDITOR],
subCommands: [
{
value: "utools.screenToDipRect",
label: "物理区域转DIP区域",
desc: "屏幕物理区域转 DIP 区域",
icon: "transform",
},
{
value: "utools.dipToScreenRect",
label: "DIP区域转物理区域",
desc: "DIP 区域转屏幕物理区域",
icon: "transform",
},
],
},
],
};

View File

@ -1,3 +1,5 @@
import { newVarInputVal } from "js/composer/varInputValManager";
export const simulateCommands = {
label: "模拟操作",
icon: "ads_click",
@ -185,5 +187,89 @@ export const simulateCommands = {
config: [],
isAsync: true,
},
{
value: "quickcomposer.simulate.screenColorPick",
label: "屏幕取色",
desc: "获取用户选择的颜色,会弹出一个系统取色器",
icon: "colorize",
isAsync: true,
outputVariable: "{hex,rgb}",
saveOutput: true,
},
{
value: "quickcomposer.simulate.captureScreen",
label: "屏幕截图",
desc: "屏幕截图,进行区域截图或全屏截图",
icon: "screenshot_monitor",
isAsync: true,
outputVariable: "base64Data",
saveOutput: true,
config: [
{
key: "range",
label: "截图范围",
type: "buttonGroup",
options: [
{
label: "全屏截图",
value: "fullscreen",
},
{
label: "区域截图",
value: "area",
},
],
defaultValue: "fullscreen",
width: 12,
},
],
subCommands: [
{
label: "保存到dataUrl",
value: "quickcomposer.simulate.captureScreen",
icon: "link",
},
{
label: "保存到文件",
value: "quickcomposer.simulate.captureScreenToFile",
icon: "file_copy",
config: [
{
key: "path",
label: "截图保存路径",
type: "varInput",
defaultValue: newVarInputVal(
"str",
`${window.utools.getPath("desktop")}${
utools.isWindows() ? "\\" : "/"
}quickcommand_screenshot.png`
),
options: {
dialog: {
type: "save",
options: {
title: "选择保存路径",
properties: ["openFile", "showHiddenFiles"],
filters: [
{
name: "PNG",
extensions: ["png"],
},
],
},
},
},
icon: "description",
width: 12,
},
],
},
{
label: "复制到剪贴板",
value: "quickcomposer.simulate.captureScreenToClipboard",
icon: "content_copy",
},
],
},
],
};

View File

@ -6,8 +6,13 @@ export const systemCommands = {
defaultOpened: false,
commands: [
{
value: "electron.clipboard.writeText",
label: "将内容写入剪贴板",
value: "utools.copyText",
label: "写入剪贴板",
subCommands: [
{
value: "utools.copyText",
label: "写入文本",
icon: "content_copy",
config: [
{
key: "content",
@ -17,9 +22,59 @@ export const systemCommands = {
},
],
},
{
value: "utools.copyImage",
label: "写入图片",
icon: "image",
config: [
{
key: "image",
label: "图片路径/base64",
type: "varInput",
icon: "image",
options: {
dialog: {
type: "open",
options: {
title: "选择图片",
properties: ["openFile", "showHiddenFiles"],
},
},
},
},
],
},
{
value: "utools.copyFile",
label: "写入文件",
icon: "file_copy",
config: [
{
key: "file",
label: "文件路径",
type: "varInput",
icon: "file_copy",
options: {
dialog: {
type: "open",
options: {
title: "选择文件",
properties: [
"openFile",
"showHiddenFiles",
"multiSelections",
],
},
},
},
},
],
},
],
},
{
value: "electron.clipboard.readText",
label: "获取剪贴板内容",
label: "读取剪贴板",
outputVariable: "clipboardContent",
saveOutput: true,
subCommands: [
@ -33,6 +88,11 @@ export const systemCommands = {
label: "剪贴板图片",
icon: "image",
},
{
value: "utools.getCopyedFiles",
label: "剪贴板文件",
icon: "file_copy",
},
{
value: "electron.clipboard.readRTF",
label: "剪贴板RTF",

View File

@ -110,6 +110,7 @@ export const utoolsCommands = {
type: "dictEditor",
icon: "settings",
options: {
disableAdd: true,
fixedKeys: [
{
value: "forward",