mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-29 20:32:44 +08:00
实现基本的命令可视化编排
This commit is contained in:
parent
dece5dbd11
commit
57de0b651b
@ -38,7 +38,12 @@
|
|||||||
<q-btn-group unelevated class="button-group">
|
<q-btn-group unelevated class="button-group">
|
||||||
<template v-if="modelValue.program === 'quickcommand'">
|
<template v-if="modelValue.program === 'quickcommand'">
|
||||||
<q-btn
|
<q-btn
|
||||||
v-for="(item, index) in ['keyboard', 'rocket_launch', 'help_center']"
|
v-for="(item, index) in [
|
||||||
|
'keyboard',
|
||||||
|
'rocket_launch',
|
||||||
|
'help_center',
|
||||||
|
'view_timeline',
|
||||||
|
]"
|
||||||
:key="index"
|
:key="index"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
@ -48,7 +53,7 @@
|
|||||||
@click="handleQuickCommandAction(index)"
|
@click="handleQuickCommandAction(index)"
|
||||||
>
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
{{ ["录制按键", "快捷动作", "查看文档"][index] }}
|
{{ ["录制按键", "快捷动作", "查看文档", "可视化编排"][index] }}
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</template>
|
</template>
|
||||||
@ -163,18 +168,32 @@
|
|||||||
<q-dialog v-model="showRecorder" position="bottom">
|
<q-dialog v-model="showRecorder" position="bottom">
|
||||||
<KeyRecorder @sendKeys="addAction" />
|
<KeyRecorder @sendKeys="addAction" />
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
<q-dialog v-model="showComposer" maximized>
|
||||||
|
<q-card class="full-height">
|
||||||
|
<q-card-section class="q-pa-md full-height">
|
||||||
|
<CommandComposer
|
||||||
|
ref="composer"
|
||||||
|
@run="handleComposerRun"
|
||||||
|
@apply="handleComposerApply"
|
||||||
|
@update:model-value="showComposer = false"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import QuickAction from "components/popup/QuickAction";
|
import QuickAction from "components/popup/QuickAction";
|
||||||
import KeyRecorder from "components/popup/KeyRecorder";
|
import KeyRecorder from "components/popup/KeyRecorder";
|
||||||
|
import CommandComposer from "components/editor/composer/CommandComposer.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CommandLanguageBar",
|
name: "CommandLanguageBar",
|
||||||
components: {
|
components: {
|
||||||
QuickAction,
|
QuickAction,
|
||||||
KeyRecorder,
|
KeyRecorder,
|
||||||
|
CommandComposer,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@ -198,6 +217,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
showActions: false,
|
showActions: false,
|
||||||
showRecorder: false,
|
showRecorder: false,
|
||||||
|
showComposer: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: [
|
emits: [
|
||||||
@ -267,6 +287,7 @@ export default {
|
|||||||
() => (this.showRecorder = true),
|
() => (this.showRecorder = true),
|
||||||
() => (this.showActions = true),
|
() => (this.showActions = true),
|
||||||
() => this.showHelp(),
|
() => this.showHelp(),
|
||||||
|
() => (this.showComposer = true),
|
||||||
];
|
];
|
||||||
actions[index]();
|
actions[index]();
|
||||||
},
|
},
|
||||||
@ -276,6 +297,20 @@ export default {
|
|||||||
showHelp() {
|
showHelp() {
|
||||||
window.showUb.docs();
|
window.showUb.docs();
|
||||||
},
|
},
|
||||||
|
handleComposerRun(code) {
|
||||||
|
this.$emit('add-action', code);
|
||||||
|
},
|
||||||
|
handleComposerApply(code) {
|
||||||
|
this.$emit('add-action', code);
|
||||||
|
this.showComposer = false;
|
||||||
|
},
|
||||||
|
applyComposerCommands() {
|
||||||
|
if (this.$refs.composer) {
|
||||||
|
const code = this.$refs.composer.generateCode();
|
||||||
|
this.$emit('add-action', code);
|
||||||
|
}
|
||||||
|
this.showComposer = false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
159
src/components/editor/composer/CommandComposer.vue
Normal file
159
src/components/editor/composer/CommandComposer.vue
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="command-composer">
|
||||||
|
<!-- 固定头部 -->
|
||||||
|
<div class="composer-header q-pa-sm row items-center bg-white">
|
||||||
|
<div class="text-h6 text-weight-medium">可视化命令编排</div>
|
||||||
|
<q-space />
|
||||||
|
<q-btn flat round dense icon="close" v-close-popup />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 主体内容 -->
|
||||||
|
<div class="composer-body row no-wrap q-pa-sm">
|
||||||
|
<!-- 左侧命令列表 -->
|
||||||
|
<div class="col-3">
|
||||||
|
<div class="text-subtitle1 q-pb-sm">可用命令</div>
|
||||||
|
<q-scroll-area style="height: calc(100vh - 200px)">
|
||||||
|
<ComposerList
|
||||||
|
:commands="availableCommands"
|
||||||
|
@add-command="addCommand"
|
||||||
|
/>
|
||||||
|
</q-scroll-area>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧命令流程 -->
|
||||||
|
<div class="col q-pl-md">
|
||||||
|
<div class="text-subtitle1 q-pb-sm">命令流程</div>
|
||||||
|
<q-scroll-area style="height: calc(100vh - 200px)">
|
||||||
|
<ComposerFlow v-model="commandFlow" @add-command="addCommand" />
|
||||||
|
</q-scroll-area>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 固定底部 -->
|
||||||
|
<div class="composer-footer q-pa-sm row items-center justify-end bg-white">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
color="primary"
|
||||||
|
label="运行"
|
||||||
|
icon="play_arrow"
|
||||||
|
class="q-mr-sm"
|
||||||
|
@click="runCommands"
|
||||||
|
/>
|
||||||
|
<q-btn flat label="取消" v-close-popup />
|
||||||
|
<q-btn unelevated color="primary" label="确认" @click="applyCommands" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import ComposerList from "./ComposerList.vue";
|
||||||
|
import ComposerFlow from "./ComposerFlow.vue";
|
||||||
|
import { commandCategories } from "./composerConfig";
|
||||||
|
|
||||||
|
// 从commandCategories中提取所有命令
|
||||||
|
const availableCommands = commandCategories.reduce((commands, category) => {
|
||||||
|
return commands.concat(
|
||||||
|
category.commands.map((cmd) => ({
|
||||||
|
type: category.label,
|
||||||
|
...cmd,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "CommandComposer",
|
||||||
|
components: {
|
||||||
|
ComposerList,
|
||||||
|
ComposerFlow,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
commandFlow: [],
|
||||||
|
nextId: 1,
|
||||||
|
availableCommands,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
addCommand(action) {
|
||||||
|
this.commandFlow.push({
|
||||||
|
...action,
|
||||||
|
id: this.nextId++,
|
||||||
|
argv: "",
|
||||||
|
saveOutput: false,
|
||||||
|
useOutput: null,
|
||||||
|
cmd: action.value || action.cmd,
|
||||||
|
value: action.value || action.cmd,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
generateCode() {
|
||||||
|
let code = [];
|
||||||
|
let outputVars = new Map();
|
||||||
|
|
||||||
|
this.commandFlow.forEach((cmd, index) => {
|
||||||
|
let line = "";
|
||||||
|
if (cmd.saveOutput) {
|
||||||
|
const varName = `output${index}`;
|
||||||
|
outputVars.set(index, varName);
|
||||||
|
line += `let ${varName} = `;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd.useOutput !== null) {
|
||||||
|
const inputVar = outputVars.get(cmd.useOutput);
|
||||||
|
line += `${cmd.value}(${inputVar})`;
|
||||||
|
} else {
|
||||||
|
const argv =
|
||||||
|
cmd.value !== "quickcommand.sleep" ? `"${cmd.argv}"` : cmd.argv;
|
||||||
|
line += `${cmd.value}(${argv})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.push(line);
|
||||||
|
});
|
||||||
|
|
||||||
|
return code.join("\n");
|
||||||
|
},
|
||||||
|
runCommands() {
|
||||||
|
const code = this.generateCode();
|
||||||
|
this.$emit("run", code);
|
||||||
|
},
|
||||||
|
applyCommands() {
|
||||||
|
const code = this.generateCode();
|
||||||
|
this.$emit("apply", code);
|
||||||
|
this.$emit("update:modelValue", false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.command-composer {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composer-header {
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composer-body {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.composer-footer {
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动美化 */
|
||||||
|
:deep(.q-scrollarea__thumb) {
|
||||||
|
width: 6px;
|
||||||
|
opacity: 0.4;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.q-scrollarea__thumb:hover) {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
</style>
|
176
src/components/editor/composer/ComposerCard.vue
Normal file
176
src/components/editor/composer/ComposerCard.vue
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<div class="composer-card q-mb-sm">
|
||||||
|
<q-card flat bordered>
|
||||||
|
<q-card-section horizontal class="q-pa-sm">
|
||||||
|
<!-- 拖拽手柄 -->
|
||||||
|
<div class="drag-handle cursor-move q-mr-sm">
|
||||||
|
<q-icon name="drag_indicator" size="24px" class="text-grey-6" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col">
|
||||||
|
<!-- 命令标题和描述 -->
|
||||||
|
<div class="row items-center q-mb-sm">
|
||||||
|
<div class="text-subtitle1">{{ command.label }}</div>
|
||||||
|
<q-space />
|
||||||
|
<!-- 输出开关 -->
|
||||||
|
<q-toggle
|
||||||
|
v-if="hasOutput"
|
||||||
|
v-model="saveOutputLocal"
|
||||||
|
label="保存输出"
|
||||||
|
dense
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="close"
|
||||||
|
@click="$emit('remove')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 参数输入 -->
|
||||||
|
<div class="row items-center">
|
||||||
|
<!-- 使用上一个命令的输出 -->
|
||||||
|
<template v-if="canUseOutput && availableOutputs.length > 0">
|
||||||
|
<q-select
|
||||||
|
v-model="useOutputLocal"
|
||||||
|
:options="availableOutputs"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
class="col"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
clearable
|
||||||
|
:label="placeholder"
|
||||||
|
@clear="handleClearOutput"
|
||||||
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<q-icon name="input" />
|
||||||
|
</template>
|
||||||
|
<template v-slot:selected-item="scope">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-icon name="output" color="primary" size="xs" class="q-mr-xs" />
|
||||||
|
{{ scope.opt.label }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</q-select>
|
||||||
|
</template>
|
||||||
|
<!-- 按键编辑器 -->
|
||||||
|
<template v-else-if="command.hasKeyRecorder">
|
||||||
|
<KeyEditor v-model="argvLocal" class="col" />
|
||||||
|
</template>
|
||||||
|
<!-- 普通参数输入 -->
|
||||||
|
<template v-else>
|
||||||
|
<q-input
|
||||||
|
v-model="argvLocal"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
class="col"
|
||||||
|
:label="placeholder"
|
||||||
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<q-icon name="code" />
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import KeyEditor from './KeyEditor.vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ComposerCard',
|
||||||
|
components: {
|
||||||
|
KeyEditor
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
command: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
hasOutput: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
canUseOutput: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
availableOutputs: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showKeyRecorder: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['remove', 'toggle-output', 'update:argv', 'update:use-output'],
|
||||||
|
computed: {
|
||||||
|
saveOutputLocal: {
|
||||||
|
get() {
|
||||||
|
return this.command.saveOutput
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit('toggle-output')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
argvLocal: {
|
||||||
|
get() {
|
||||||
|
return this.command.argv
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit('update:argv', value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
useOutputLocal: {
|
||||||
|
get() {
|
||||||
|
return this.command.useOutput
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit('update:use-output', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleClearOutput() {
|
||||||
|
this.$emit('update:use-output', null)
|
||||||
|
},
|
||||||
|
handleKeyRecord(keys) {
|
||||||
|
this.showKeyRecorder = false;
|
||||||
|
// 从keyTap("a","control")格式中提取参数
|
||||||
|
const matches = keys.match(/keyTap\((.*)\)/)
|
||||||
|
if (matches && matches[1]) {
|
||||||
|
this.$emit('update:argv', matches[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.composer-card {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle:hover {
|
||||||
|
color: var(--q-primary);
|
||||||
|
}
|
||||||
|
</style>
|
204
src/components/editor/composer/ComposerFlow.vue
Normal file
204
src/components/editor/composer/ComposerFlow.vue
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
<template>
|
||||||
|
<div class="composer-flow">
|
||||||
|
<div
|
||||||
|
class="command-flow-container"
|
||||||
|
@dragover.prevent
|
||||||
|
@drop="onDrop"
|
||||||
|
>
|
||||||
|
<draggable
|
||||||
|
v-model="commands"
|
||||||
|
group="commands"
|
||||||
|
item-key="id"
|
||||||
|
class="flow-list"
|
||||||
|
handle=".drag-handle"
|
||||||
|
:animation="200"
|
||||||
|
>
|
||||||
|
<template #item="{ element, index }">
|
||||||
|
<transition
|
||||||
|
name="slide-fade"
|
||||||
|
mode="out-in"
|
||||||
|
appear
|
||||||
|
>
|
||||||
|
<div :key="element.id" class="flow-item">
|
||||||
|
<ComposerCard
|
||||||
|
:command="element"
|
||||||
|
:has-output="hasOutput(element)"
|
||||||
|
:can-use-output="canUseOutput(element, index)"
|
||||||
|
:available-outputs="getAvailableOutputs(index)"
|
||||||
|
:placeholder="getPlaceholder(element, index)"
|
||||||
|
@remove="removeCommand(index)"
|
||||||
|
@toggle-output="toggleSaveOutput(index)"
|
||||||
|
@update:argv="(val) => handleArgvChange(index, val)"
|
||||||
|
@update:use-output="(val) => handleUseOutputChange(index, val)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
<div v-if="commands.length === 0" class="empty-flow">
|
||||||
|
<div class="text-center text-grey-6">
|
||||||
|
<q-icon name="drag_indicator" size="32px" />
|
||||||
|
<div class="text-body2 q-mt-sm">从左侧拖拽命令到这里开始编排</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import ComposerCard from './ComposerCard.vue'
|
||||||
|
import { commandsWithOutput, commandsAcceptOutput } from './composerConfig'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ComposerFlow',
|
||||||
|
components: {
|
||||||
|
draggable,
|
||||||
|
ComposerCard
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
emits: ['update:modelValue', 'add-command'],
|
||||||
|
computed: {
|
||||||
|
commands: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit('update:modelValue', value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onDrop(event) {
|
||||||
|
const actionData = JSON.parse(event.dataTransfer.getData('action'))
|
||||||
|
this.$emit('add-command', actionData)
|
||||||
|
document.querySelectorAll('.dragging').forEach(el => {
|
||||||
|
el.classList.remove('dragging')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeCommand(index) {
|
||||||
|
const newCommands = [...this.commands]
|
||||||
|
newCommands.splice(index, 1)
|
||||||
|
this.$emit('update:modelValue', newCommands)
|
||||||
|
},
|
||||||
|
hasOutput(command) {
|
||||||
|
return commandsWithOutput[command.value] || false
|
||||||
|
},
|
||||||
|
canUseOutput(command, index) {
|
||||||
|
return commandsAcceptOutput[command.value] && this.getAvailableOutputs(index).length > 0
|
||||||
|
},
|
||||||
|
getAvailableOutputs(currentIndex) {
|
||||||
|
return this.commands
|
||||||
|
.slice(0, currentIndex)
|
||||||
|
.map((cmd, index) => ({
|
||||||
|
label: `${cmd.label} 的输出`,
|
||||||
|
value: index,
|
||||||
|
disable: !cmd.saveOutput
|
||||||
|
}))
|
||||||
|
.filter(item => !item.disable)
|
||||||
|
},
|
||||||
|
toggleSaveOutput(index) {
|
||||||
|
const newCommands = [...this.commands]
|
||||||
|
newCommands[index].saveOutput = !newCommands[index].saveOutput
|
||||||
|
if (!newCommands[index].saveOutput) {
|
||||||
|
newCommands.forEach((cmd, i) => {
|
||||||
|
if (i > index && cmd.useOutput === index) {
|
||||||
|
cmd.useOutput = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
this.$emit('update:modelValue', newCommands)
|
||||||
|
},
|
||||||
|
handleArgvChange(index, value) {
|
||||||
|
const newCommands = [...this.commands]
|
||||||
|
newCommands[index].argv = value
|
||||||
|
this.$emit('update:modelValue', newCommands)
|
||||||
|
},
|
||||||
|
handleUseOutputChange(index, value) {
|
||||||
|
const newCommands = [...this.commands]
|
||||||
|
newCommands[index].useOutput = value
|
||||||
|
if (value !== null) {
|
||||||
|
newCommands[index].argv = ''
|
||||||
|
}
|
||||||
|
this.$emit('update:modelValue', newCommands)
|
||||||
|
},
|
||||||
|
getPlaceholder(element, index) {
|
||||||
|
if (element.useOutput !== null) {
|
||||||
|
return `使用 ${this.commands[element.useOutput].label} 的输出`
|
||||||
|
}
|
||||||
|
return element.desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.composer-flow {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-flow-container {
|
||||||
|
min-height: 100px;
|
||||||
|
padding: 8px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-flow-container:empty {
|
||||||
|
border: 2px dashed #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-list {
|
||||||
|
min-height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-flow {
|
||||||
|
height: 100px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 2px dashed #e0e0e0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 8px 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-flow:hover {
|
||||||
|
border-color: #bdbdbd;
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 拖拽时的视觉反馈 */
|
||||||
|
.command-flow-container.drag-over {
|
||||||
|
background-color: #f0f4ff;
|
||||||
|
border-color: #2196f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滑动淡出动画 */
|
||||||
|
.slide-fade-enter-active,
|
||||||
|
.slide-fade-leave-active {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.slide-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow-item {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
</style>
|
118
src/components/editor/composer/ComposerList.vue
Normal file
118
src/components/editor/composer/ComposerList.vue
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<div class="composer-list">
|
||||||
|
<q-list bordered separator class="rounded-borders">
|
||||||
|
<template v-for="category in commandCategories" :key="category.label">
|
||||||
|
<q-item-label header class="q-py-sm bg-grey-2">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-icon :name="category.icon" color="primary" size="sm" class="q-mr-sm" />
|
||||||
|
<span class="text-weight-medium">{{ category.label }}</span>
|
||||||
|
</div>
|
||||||
|
</q-item-label>
|
||||||
|
|
||||||
|
<q-item
|
||||||
|
v-for="command in getCategoryCommands(category)"
|
||||||
|
:key="command.value"
|
||||||
|
class="command-item q-py-xs"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="onDragStart($event, command)"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label class="text-weight-medium">{{ command.label }}</q-item-label>
|
||||||
|
<q-item-label caption>{{ command.desc }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section side>
|
||||||
|
<q-icon name="drag_indicator" color="grey-6" size="16px" />
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
</q-list>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
import { commandCategories } from './composerConfig'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'ComposerList',
|
||||||
|
props: {
|
||||||
|
commands: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
commandCategories
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCategoryCommands(category) {
|
||||||
|
return this.commands.filter(cmd =>
|
||||||
|
category.commands.some(catCmd => catCmd.value === cmd.value || catCmd.value === cmd.cmd)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onDragStart(event, command) {
|
||||||
|
event.dataTransfer.setData('action', JSON.stringify({
|
||||||
|
...command,
|
||||||
|
cmd: command.value || command.cmd
|
||||||
|
}))
|
||||||
|
event.target.classList.add('dragging')
|
||||||
|
event.target.addEventListener('dragend', () => {
|
||||||
|
event.target.classList.remove('dragging')
|
||||||
|
}, { once: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.composer-list {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item {
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 4px 8px;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
background: white;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
transform: translateX(4px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-item.dragging {
|
||||||
|
opacity: 0.5;
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分类标题样式 */
|
||||||
|
.q-item-label.header {
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
border-bottom: 1px solid #e0e0e0;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色模式适配 */
|
||||||
|
.body--dark .command-item {
|
||||||
|
border-color: #424242;
|
||||||
|
background: #1d1d1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .command-item:hover {
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .q-item-label.header {
|
||||||
|
border-color: #424242;
|
||||||
|
background-color: #2d2d2d;
|
||||||
|
}
|
||||||
|
</style>
|
362
src/components/editor/composer/KeyEditor.vue
Normal file
362
src/components/editor/composer/KeyEditor.vue
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
<template>
|
||||||
|
<div class="key-editor">
|
||||||
|
<!-- 按键输入框 -->
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
readonly
|
||||||
|
class="col"
|
||||||
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<!-- 修饰键 -->
|
||||||
|
<div class="row items-center q-gutter-x-xs">
|
||||||
|
<q-chip
|
||||||
|
v-for="(active, key) in modifiers"
|
||||||
|
:key="key"
|
||||||
|
:color="active ? 'primary' : 'grey-4'"
|
||||||
|
:text-color="active ? 'white' : 'grey-7'"
|
||||||
|
dense
|
||||||
|
clickable
|
||||||
|
class="modifier-chip"
|
||||||
|
@click="toggleModifier(key)"
|
||||||
|
>
|
||||||
|
{{ modifierLabels[key] }}
|
||||||
|
</q-chip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 主按键显示 -->
|
||||||
|
<template v-slot:default>
|
||||||
|
<div class="main-key-container">
|
||||||
|
<div class="main-key">
|
||||||
|
{{ mainKeyDisplay }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-separator vertical inset />
|
||||||
|
<!-- 选择按钮 -->
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="edit"
|
||||||
|
@click="showKeySelect = true"
|
||||||
|
>
|
||||||
|
<q-tooltip>选择按键</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<q-separator vertical inset />
|
||||||
|
<!-- 录制按钮 -->
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
:icon="isRecording ? 'fiber_manual_record' : 'radio_button_unchecked'"
|
||||||
|
:color="isRecording ? 'negative' : 'primary'"
|
||||||
|
@click="toggleRecording"
|
||||||
|
>
|
||||||
|
<q-tooltip>{{ isRecording ? '停止录制' : '开始录制' }}</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<!-- 按键选择对话框 -->
|
||||||
|
<q-dialog v-model="showKeySelect">
|
||||||
|
<q-card style="min-width: 300px">
|
||||||
|
<q-card-section>
|
||||||
|
<div class="text-h6">选择按键</div>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="q-gutter-y-md">
|
||||||
|
<!-- 主按键选择 -->
|
||||||
|
<q-select
|
||||||
|
v-model="mainKey"
|
||||||
|
:options="commonKeys"
|
||||||
|
label="选择用按键"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
v-model="customKey"
|
||||||
|
label="或输入自定义按键"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
@update:model-value="mainKey = $event"
|
||||||
|
/>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-actions align="right">
|
||||||
|
<q-btn flat label="确定" color="primary" v-close-popup />
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
// 检测操作系统
|
||||||
|
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'KeyEditor',
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isRecording: false,
|
||||||
|
showKeySelect: false,
|
||||||
|
mainKey: '',
|
||||||
|
customKey: '',
|
||||||
|
modifiers: {
|
||||||
|
control: false,
|
||||||
|
alt: false,
|
||||||
|
shift: false,
|
||||||
|
command: false
|
||||||
|
},
|
||||||
|
modifierLabels: isMac ? {
|
||||||
|
control: '⌃',
|
||||||
|
alt: '⌥',
|
||||||
|
shift: '⇧',
|
||||||
|
command: '⌘'
|
||||||
|
} : {
|
||||||
|
control: 'Ctrl',
|
||||||
|
alt: 'Alt',
|
||||||
|
shift: 'Shift',
|
||||||
|
command: 'Win'
|
||||||
|
},
|
||||||
|
commonKeys: [
|
||||||
|
{ label: 'Enter ↵', value: 'enter' },
|
||||||
|
{ label: 'Tab ⇥', value: 'tab' },
|
||||||
|
{ label: 'Space', value: 'space' },
|
||||||
|
{ label: 'Backspace ⌫', value: 'backspace' },
|
||||||
|
{ label: 'Delete ⌦', value: 'delete' },
|
||||||
|
{ label: 'Escape ⎋', value: 'escape' },
|
||||||
|
{ label: '↑', value: 'up' },
|
||||||
|
{ label: '↓', value: 'down' },
|
||||||
|
{ label: '←', value: 'left' },
|
||||||
|
{ label: '→', value: 'right' },
|
||||||
|
{ label: 'Home', value: 'home' },
|
||||||
|
{ label: 'End', value: 'end' },
|
||||||
|
{ label: 'Page Up', value: 'pageup' },
|
||||||
|
{ label: 'Page Down', value: 'pagedown' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
mainKeyDisplay() {
|
||||||
|
if (!this.mainKey) return ''
|
||||||
|
// 特殊按键映射表
|
||||||
|
const specialKeyMap = {
|
||||||
|
'enter': '↵',
|
||||||
|
'tab': '⇥',
|
||||||
|
'space': '␣',
|
||||||
|
'backspace': '⌫',
|
||||||
|
'delete': '⌦',
|
||||||
|
'escape': '⎋',
|
||||||
|
'up': '↑',
|
||||||
|
'down': '↓',
|
||||||
|
'left': '←',
|
||||||
|
'right': '→'
|
||||||
|
}
|
||||||
|
return specialKeyMap[this.mainKey] ||
|
||||||
|
(this.mainKey.length === 1 ? this.mainKey.toUpperCase() :
|
||||||
|
this.mainKey.charAt(0).toUpperCase() + this.mainKey.slice(1))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
modelValue: {
|
||||||
|
immediate: true,
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.parseKeyString(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
toggleModifier(key) {
|
||||||
|
this.modifiers[key] = !this.modifiers[key]
|
||||||
|
this.updateValue()
|
||||||
|
},
|
||||||
|
toggleRecording() {
|
||||||
|
if (!this.isRecording) {
|
||||||
|
this.startRecording()
|
||||||
|
} else {
|
||||||
|
this.stopRecording()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
startRecording() {
|
||||||
|
this.isRecording = true
|
||||||
|
let lastKeyTime = 0
|
||||||
|
let lastKey = null
|
||||||
|
|
||||||
|
this.recordEvent = (event) => {
|
||||||
|
event.preventDefault()
|
||||||
|
const currentTime = Date.now()
|
||||||
|
|
||||||
|
// 重置所有修饰键状态
|
||||||
|
Object.keys(this.modifiers).forEach(key => {
|
||||||
|
this.modifiers[key] = false
|
||||||
|
})
|
||||||
|
|
||||||
|
// 根据操作系统设置修饰键
|
||||||
|
if (isMac) {
|
||||||
|
if (event.metaKey) this.modifiers.command = true
|
||||||
|
if (event.ctrlKey) this.modifiers.control = true
|
||||||
|
} else {
|
||||||
|
if (event.ctrlKey) this.modifiers.control = true
|
||||||
|
if (event.metaKey || event.winKey) this.modifiers.command = true
|
||||||
|
}
|
||||||
|
if (event.altKey) this.modifiers.alt = true
|
||||||
|
if (event.shiftKey) this.modifiers.shift = true
|
||||||
|
|
||||||
|
// 设置主按键
|
||||||
|
let key = null
|
||||||
|
|
||||||
|
// 处理字母键
|
||||||
|
if (event.code.startsWith('Key')) {
|
||||||
|
key = event.code.slice(-1).toLowerCase()
|
||||||
|
}
|
||||||
|
// 处理数字键
|
||||||
|
else if (event.code.startsWith('Digit')) {
|
||||||
|
key = event.code.slice(-1)
|
||||||
|
}
|
||||||
|
// 处理功能键
|
||||||
|
else if (event.code.startsWith('F') && !isNaN(event.code.slice(1))) {
|
||||||
|
key = event.code.toLowerCase()
|
||||||
|
}
|
||||||
|
// 处理其他特殊键
|
||||||
|
else {
|
||||||
|
const keyMap = {
|
||||||
|
'ArrowUp': 'up',
|
||||||
|
'ArrowDown': 'down',
|
||||||
|
'ArrowLeft': 'left',
|
||||||
|
'ArrowRight': 'right',
|
||||||
|
'Enter': 'enter',
|
||||||
|
'Space': 'space',
|
||||||
|
'Escape': 'escape',
|
||||||
|
'Delete': 'delete',
|
||||||
|
'Backspace': 'backspace',
|
||||||
|
'Tab': 'tab',
|
||||||
|
'Home': 'home',
|
||||||
|
'End': 'end',
|
||||||
|
'PageUp': 'pageup',
|
||||||
|
'PageDown': 'pagedown',
|
||||||
|
'Control': 'control',
|
||||||
|
'Alt': 'alt',
|
||||||
|
'Shift': 'shift',
|
||||||
|
'Meta': 'command'
|
||||||
|
}
|
||||||
|
key = keyMap[event.code] || event.key.toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理双击修饰键
|
||||||
|
if (['control', 'alt', 'shift', 'command'].includes(key)) {
|
||||||
|
if (key === lastKey && (currentTime - lastKeyTime) < 500) {
|
||||||
|
this.mainKey = key
|
||||||
|
this.modifiers[key] = false // 清除修饰键状态
|
||||||
|
this.stopRecording()
|
||||||
|
this.updateValue()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastKey = key
|
||||||
|
lastKeyTime = currentTime
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理空格键和其他按键
|
||||||
|
if (key === 'space' || !['meta', 'control', 'shift', 'alt', 'command'].includes(key)) {
|
||||||
|
this.mainKey = key
|
||||||
|
this.stopRecording()
|
||||||
|
this.updateValue()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('keydown', this.recordEvent)
|
||||||
|
},
|
||||||
|
stopRecording() {
|
||||||
|
this.isRecording = false
|
||||||
|
document.removeEventListener('keydown', this.recordEvent)
|
||||||
|
},
|
||||||
|
updateValue() {
|
||||||
|
if (!this.mainKey) return
|
||||||
|
const activeModifiers = Object.entries(this.modifiers)
|
||||||
|
.filter(([_, active]) => active)
|
||||||
|
.map(([key]) => key)
|
||||||
|
// 在非 Mac 系统上,将 command 转换为 meta
|
||||||
|
.map(key => !isMac && key === 'command' ? 'meta' : key)
|
||||||
|
|
||||||
|
const args = [this.mainKey, ...activeModifiers]
|
||||||
|
this.$emit('update:modelValue', args.join('","'))
|
||||||
|
},
|
||||||
|
parseKeyString(val) {
|
||||||
|
try {
|
||||||
|
const args = val.split('","')
|
||||||
|
if (args.length > 0) {
|
||||||
|
this.mainKey = args[0]
|
||||||
|
Object.keys(this.modifiers).forEach(key => {
|
||||||
|
// 在非 Mac 系统上,将 meta 转换为 command
|
||||||
|
const modKey = !isMac && args.includes('meta') ? 'command' : key
|
||||||
|
this.modifiers[key] = args.includes(modKey)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to parse key string:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.key-editor {
|
||||||
|
padding: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modifier-chip {
|
||||||
|
height: 24px;
|
||||||
|
font-size: 13px;
|
||||||
|
margin: 0 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-key-container {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-key {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--q-primary);
|
||||||
|
line-height: 24px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.q-field__control) {
|
||||||
|
padding: 0 8px;
|
||||||
|
min-height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.q-field__prepend) {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.q-field__native) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.q-field__control-container) {
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
132
src/components/editor/composer/composerConfig.js
Normal file
132
src/components/editor/composer/composerConfig.js
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// 定义命令图标映射
|
||||||
|
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'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义命令分类
|
||||||
|
export const commandCategories = [
|
||||||
|
{
|
||||||
|
label: '文件操作',
|
||||||
|
icon: 'folder',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
value: "open",
|
||||||
|
label: "打开文件/文件夹/软件",
|
||||||
|
desc: "文件、文件夹或软件的绝对路径",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "locate",
|
||||||
|
label: "在文件管理器中定位文件",
|
||||||
|
desc: "要在文件管理器里显示的文件路径",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '网络操作',
|
||||||
|
icon: 'language',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
value: "visit",
|
||||||
|
label: "用默认浏览器打开网址",
|
||||||
|
desc: "要访问的网址链接",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "utools.ubrowser.goto",
|
||||||
|
label: "用ubrowser打开网址",
|
||||||
|
desc: "要访问的网址链接",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '系统操作',
|
||||||
|
icon: 'computer',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
value: "system",
|
||||||
|
label: "执行系统命令",
|
||||||
|
desc: "要执行的命令行",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "copyTo",
|
||||||
|
label: "将内容写入剪贴板",
|
||||||
|
desc: "要写入剪切板的内容",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '消息通知',
|
||||||
|
icon: 'notifications',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
value: "message",
|
||||||
|
label: "发送系统消息",
|
||||||
|
desc: "要发送的系统消息文本",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "alert",
|
||||||
|
label: "弹窗显示消息",
|
||||||
|
desc: "要弹窗显示的消息文本",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "send",
|
||||||
|
label: "发送文本到活动窗口",
|
||||||
|
desc: "要发送到窗口的文本内容",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '其他功能',
|
||||||
|
icon: 'more_horiz',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
value: "utools.redirect",
|
||||||
|
label: "转至指定插件",
|
||||||
|
desc: "要跳转至的插件名称",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "quickcommand.sleep",
|
||||||
|
label: "添加延时",
|
||||||
|
desc: "延迟的毫秒数",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '按键操作',
|
||||||
|
icon: 'keyboard',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
value: "keyTap",
|
||||||
|
label: "模拟按键",
|
||||||
|
desc: "模拟键盘按键",
|
||||||
|
hasKeyRecorder: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// 定义哪些命令可以产生输出
|
||||||
|
export const commandsWithOutput = {
|
||||||
|
'system': true,
|
||||||
|
'open': true,
|
||||||
|
'locate': true,
|
||||||
|
'copyTo': true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义哪些命令可以接收输出
|
||||||
|
export const commandsAcceptOutput = {
|
||||||
|
'message': true,
|
||||||
|
'alert': true,
|
||||||
|
'send': true,
|
||||||
|
'copyTo': true,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user