mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-28 20:02:44 +08:00
完善变量输入输出,新增预览窗口
This commit is contained in:
parent
e0eb337b1b
commit
cdfb2b502f
159
src/components/editor/composer/CodePreview.vue
Normal file
159
src/components/editor/composer/CodePreview.vue
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
<template>
|
||||||
|
<div class="code-preview">
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="preview"
|
||||||
|
class="preview-btn"
|
||||||
|
@mouseenter="handleMouseEnter"
|
||||||
|
@mouseleave="handleMouseLeave"
|
||||||
|
>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<transition name="preview-fade">
|
||||||
|
<div v-if="isVisible" class="preview-popup">
|
||||||
|
<div class="preview-header">
|
||||||
|
<q-icon name="code" size="16px" class="q-mr-xs" />
|
||||||
|
<span>预览代码</span>
|
||||||
|
</div>
|
||||||
|
<pre class="preview-code"><code>{{ code }}</code></pre>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "CodePreview",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
generateCode: {
|
||||||
|
type: Function,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isVisible: false,
|
||||||
|
code: "",
|
||||||
|
previewTimer: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
handleMouseEnter() {
|
||||||
|
this.previewTimer = setTimeout(() => {
|
||||||
|
this.code = this.generateCode();
|
||||||
|
this.isVisible = true;
|
||||||
|
}, 200);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleMouseLeave() {
|
||||||
|
clearTimeout(this.previewTimer);
|
||||||
|
this.isVisible = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeUnmount() {
|
||||||
|
clearTimeout(this.previewTimer);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.code-preview {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-btn {
|
||||||
|
color: var(--q-primary);
|
||||||
|
opacity: 0.7;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-popup {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: calc(100% + 12px);
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 600px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
z-index: 1000;
|
||||||
|
transform-origin: center right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-header {
|
||||||
|
padding: 10px 14px;
|
||||||
|
background: rgba(var(--q-primary-rgb), 0.03);
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--q-primary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-code {
|
||||||
|
margin: 0;
|
||||||
|
padding: 14px;
|
||||||
|
font-family: consolas, monaco, monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义滚动条 */
|
||||||
|
.preview-code::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-code::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--q-primary-opacity-20);
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-code::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--q-primary-opacity-30);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 过渡动画 */
|
||||||
|
.preview-fade-enter-active,
|
||||||
|
.preview-fade-leave-active {
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-fade-enter-from,
|
||||||
|
.preview-fade-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px) scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色模式适配 */
|
||||||
|
.body--dark .preview-popup {
|
||||||
|
background: #1d1d1d;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .preview-header {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .preview-code {
|
||||||
|
color: #e0e0e0;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,10 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="command-composer">
|
<div class="command-composer">
|
||||||
<!-- 主体内容 -->
|
<!-- 主体内容 -->
|
||||||
<div class="composer-body row no-wrap q-pa-sm">
|
<div class="composer-body row no-wrap q-pa-sm q-gutter-sm">
|
||||||
<!-- 左侧命令列表 -->
|
<!-- 左侧命令列表 -->
|
||||||
<div class="col-3 command-section">
|
<div class="col-3 command-section">
|
||||||
<div class="text-subtitle1 q-pb-sm">可用命令</div>
|
<div class="section-header">
|
||||||
|
<q-icon name="list" size="20px" class="q-mr-sm text-primary" />
|
||||||
|
<span class="text-subtitle1">可用命令</span>
|
||||||
|
</div>
|
||||||
<q-scroll-area class="command-scroll">
|
<q-scroll-area class="command-scroll">
|
||||||
<ComposerList
|
<ComposerList
|
||||||
:commands="availableCommands"
|
:commands="availableCommands"
|
||||||
@ -15,7 +18,12 @@
|
|||||||
|
|
||||||
<!-- 右侧命令流程 -->
|
<!-- 右侧命令流程 -->
|
||||||
<div class="col q-pl-md command-section">
|
<div class="col q-pl-md command-section">
|
||||||
<div class="text-subtitle1 q-pb-sm">命令流程</div>
|
<div class="section-header">
|
||||||
|
<q-icon name="timeline" size="20px" class="q-mr-sm text-primary" />
|
||||||
|
<span class="text-subtitle1">命令流程</span>
|
||||||
|
<q-space />
|
||||||
|
<CodePreview :generate-code="generateCode" />
|
||||||
|
</div>
|
||||||
<q-scroll-area class="command-scroll">
|
<q-scroll-area class="command-scroll">
|
||||||
<ComposerFlow v-model="commandFlow" @add-command="addCommand" />
|
<ComposerFlow v-model="commandFlow" @add-command="addCommand" />
|
||||||
</q-scroll-area>
|
</q-scroll-area>
|
||||||
@ -23,20 +31,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 固定底部 -->
|
<!-- 固定底部 -->
|
||||||
<div class="composer-footer q-pa-sm q-gutter-sm row justify-end">
|
<div class="composer-footer q-pa-sm row justify-end">
|
||||||
<q-btn label="取消" v-close-popup />
|
<div class="action-buttons q-gutter-sm">
|
||||||
<q-btn color="primary" label="插入" @click="handleComposer('insert')" />
|
<q-btn label="取消" v-close-popup />
|
||||||
<q-btn color="primary" label="应用" @click="handleComposer('apply')" />
|
<q-btn color="primary" label="插入" @click="handleComposer('insert')" />
|
||||||
<q-btn color="positive" label="运行" @click="handleComposer('run')" />
|
<q-btn color="primary" label="应用" @click="handleComposer('apply')" />
|
||||||
|
<q-btn color="positive" label="运行" @click="handleComposer('run')" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent, provide, ref } from "vue";
|
||||||
import ComposerList from "./ComposerList.vue";
|
import ComposerList from "./ComposerList.vue";
|
||||||
import ComposerFlow from "./ComposerFlow.vue";
|
import ComposerFlow from "./ComposerFlow.vue";
|
||||||
import { commandCategories } from "./composerConfig";
|
import CodePreview from "./CodePreview.vue";
|
||||||
|
import { commandCategories } from "js/composer/composerConfig";
|
||||||
|
|
||||||
// 从commandCategories中提取所有命令
|
// 从commandCategories中提取所有命令
|
||||||
const availableCommands = commandCategories.reduce((commands, category) => {
|
const availableCommands = commandCategories.reduce((commands, category) => {
|
||||||
@ -53,6 +64,36 @@ export default defineComponent({
|
|||||||
components: {
|
components: {
|
||||||
ComposerList,
|
ComposerList,
|
||||||
ComposerFlow,
|
ComposerFlow,
|
||||||
|
CodePreview,
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const variables = ref([]);
|
||||||
|
|
||||||
|
const addVariable = (name, command) => {
|
||||||
|
if (!variables.value.find((v) => v.name === name)) {
|
||||||
|
variables.value.push({
|
||||||
|
name,
|
||||||
|
sourceCommand: command,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeVariable = (name) => {
|
||||||
|
const index = variables.value.findIndex((v) => v.name === name);
|
||||||
|
if (index !== -1) {
|
||||||
|
variables.value.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
provide("composerVariables", variables);
|
||||||
|
provide("addVariable", addVariable);
|
||||||
|
provide("removeVariable", removeVariable);
|
||||||
|
|
||||||
|
return {
|
||||||
|
variables,
|
||||||
|
addVariable,
|
||||||
|
removeVariable,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -68,31 +109,35 @@ export default defineComponent({
|
|||||||
...action,
|
...action,
|
||||||
id: this.nextId++,
|
id: this.nextId++,
|
||||||
argv: "",
|
argv: "",
|
||||||
|
argvType: "string",
|
||||||
saveOutput: false,
|
saveOutput: false,
|
||||||
useOutput: null,
|
useOutput: null,
|
||||||
|
outputVariable: null,
|
||||||
cmd: action.value || action.cmd,
|
cmd: action.value || action.cmd,
|
||||||
value: action.value || action.cmd,
|
value: action.value || action.cmd,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
generateCode() {
|
generateCode() {
|
||||||
let code = [];
|
let code = [];
|
||||||
let outputVars = new Map();
|
|
||||||
|
|
||||||
this.commandFlow.forEach((cmd, index) => {
|
this.commandFlow.forEach((cmd) => {
|
||||||
let line = "";
|
let line = "";
|
||||||
if (cmd.saveOutput) {
|
// TODO: 切换到变量后还是string类型
|
||||||
const varName = `output${index}`;
|
console.log("Generating code for command:", cmd);
|
||||||
outputVars.set(index, varName);
|
|
||||||
line += `let ${varName} = `;
|
if (cmd.outputVariable) {
|
||||||
|
line += `let ${cmd.outputVariable} = `;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cmd.value === "ubrowser") {
|
if (cmd.value === "ubrowser") {
|
||||||
line += cmd.argv;
|
line += cmd.argv;
|
||||||
} else if (cmd.useOutput !== null) {
|
} else if (cmd.useOutput !== null) {
|
||||||
const inputVar = outputVars.get(cmd.useOutput);
|
const outputVar = this.commandFlow[cmd.useOutput].outputVariable;
|
||||||
line += `${cmd.value}(${inputVar})`;
|
line += `${cmd.value}(${outputVar})`;
|
||||||
} else {
|
} else {
|
||||||
const argv =
|
const needQuotes =
|
||||||
cmd.value !== "quickcommand.sleep" ? `"${cmd.argv}"` : cmd.argv;
|
cmd.argvType === "string" && cmd.argvType !== "variable";
|
||||||
|
const argv = needQuotes ? `"${cmd.argv}"` : cmd.argv;
|
||||||
line += `${cmd.value}(${argv})`;
|
line += `${cmd.value}(${argv})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +176,25 @@ export default defineComponent({
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .command-section {
|
||||||
|
background: #1d1d1d;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 16px;
|
||||||
|
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .section-header {
|
||||||
|
border-bottom-color: rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.command-scroll {
|
.command-scroll {
|
||||||
@ -139,16 +203,18 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.composer-footer {
|
.composer-footer {
|
||||||
border-top: 1px solid #e0e0e0;
|
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.body--dark .composer-footer {
|
.body--dark .composer-footer {
|
||||||
border-top: 1px solid #676666;
|
border-top-color: rgba(255, 255, 255, 0.1);
|
||||||
|
background: #1d1d1d;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 滚动美化 */
|
/* 滚动美化 */
|
||||||
:deep(.q-scrollarea__thumb) {
|
:deep(.q-scrollarea__thumb) {
|
||||||
width: 6px;
|
width: 2px;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.3s ease;
|
||||||
}
|
}
|
||||||
@ -157,7 +223,16 @@ export default defineComponent({
|
|||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.q-scrollarea__content) {
|
/* 动画效果 */
|
||||||
padding-right: 8px;
|
.command-section {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-section:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .command-section:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -15,13 +15,52 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="text-subtitle1">{{ command.label }}</div>
|
<div class="text-subtitle1">{{ command.label }}</div>
|
||||||
<q-space />
|
<q-space />
|
||||||
<!-- 输出开关 -->
|
|
||||||
<q-toggle
|
<!-- 输出变量设置 -->
|
||||||
v-if="hasOutput"
|
<div
|
||||||
v-model="saveOutputLocal"
|
class="output-section row items-center no-wrap"
|
||||||
label="保存输出"
|
v-if="command.saveOutput"
|
||||||
|
>
|
||||||
|
<q-input
|
||||||
|
:model-value="command.outputVariable"
|
||||||
|
@update:model-value="handleOutputVariableUpdate"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
placeholder="变量名"
|
||||||
|
class="variable-input"
|
||||||
|
style="width: 100px"
|
||||||
|
align="center"
|
||||||
|
>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
:icon="saveOutputLocal ? 'data_object' : 'output'"
|
||||||
|
:label="saveOutputLocal ? '保存到变量' : '获取输出'"
|
||||||
|
flat
|
||||||
dense
|
dense
|
||||||
/>
|
class="output-btn q-px-sm q-mr-sm"
|
||||||
|
size="sm"
|
||||||
|
@click="handleToggleOutput"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
<div class="text-body2">
|
||||||
|
{{
|
||||||
|
saveOutputLocal
|
||||||
|
? "当前命令的输出将保存到变量中"
|
||||||
|
: "点击将此命令的输出保存为变量以供后续使用"
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="text-caption text-grey-5">
|
||||||
|
{{
|
||||||
|
saveOutputLocal
|
||||||
|
? "点击取消输出到变量"
|
||||||
|
: "保存后可在其他命令中使用此变量"
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
<q-btn
|
<q-btn
|
||||||
flat
|
flat
|
||||||
round
|
round
|
||||||
@ -29,65 +68,31 @@
|
|||||||
icon="close"
|
icon="close"
|
||||||
@click="$emit('remove')"
|
@click="$emit('remove')"
|
||||||
size="sm"
|
size="sm"
|
||||||
/>
|
class="remove-btn"
|
||||||
|
>
|
||||||
|
<q-tooltip>移除此命令</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 参数输入 -->
|
<!-- 参数输入 -->
|
||||||
<div class="row items-center">
|
<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">
|
<template v-if="command.hasKeyRecorder">
|
||||||
<KeyEditor v-model="argvLocal" class="col" />
|
<KeyEditor v-model="argvLocal" class="col" />
|
||||||
</template>
|
</template>
|
||||||
<!-- UBrowser编辑器 -->
|
<!-- UBrowser编辑器 -->
|
||||||
<template v-else-if="command.hasUBrowserEditor">
|
<template v-else-if="command.hasUBrowserEditor">
|
||||||
<UBrowserEditor
|
<UBrowserEditor v-model="argvLocal" class="col" />
|
||||||
v-model="argvLocal"
|
|
||||||
class="col"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<!-- 普通参数输入 -->
|
<!-- 普通参数输入 -->
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<q-input
|
<VariableInput
|
||||||
v-model="argvLocal"
|
v-model="argvLocal"
|
||||||
dense
|
|
||||||
outlined
|
|
||||||
class="col"
|
|
||||||
:label="placeholder"
|
:label="placeholder"
|
||||||
>
|
class="col"
|
||||||
<template v-slot:prepend>
|
ref="variableInput"
|
||||||
<q-icon name="text_fields" size="18px" />
|
@update:type="handleArgvTypeUpdate"
|
||||||
</template>
|
/>
|
||||||
</q-input>
|
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -97,29 +102,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent, inject } from "vue";
|
||||||
import KeyEditor from "./KeyEditor.vue";
|
import KeyEditor from "./KeyEditor.vue";
|
||||||
import UBrowserEditor from './ubrowser/UBrowserEditor.vue';
|
import UBrowserEditor from "./ubrowser/UBrowserEditor.vue";
|
||||||
|
import VariableInput from "./VariableInput.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "ComposerCard",
|
name: "ComposerCard",
|
||||||
components: {
|
components: {
|
||||||
KeyEditor,
|
KeyEditor,
|
||||||
UBrowserEditor
|
UBrowserEditor,
|
||||||
|
VariableInput,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
command: {
|
command: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
hasOutput: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
canUseOutput: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
availableOutputs: {
|
availableOutputs: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
@ -138,7 +137,13 @@ export default defineComponent({
|
|||||||
showKeyRecorder: false,
|
showKeyRecorder: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: ["remove", "toggle-output", "update:argv", "update:use-output"],
|
emits: [
|
||||||
|
"remove",
|
||||||
|
"toggle-output",
|
||||||
|
"update:argv",
|
||||||
|
"update:use-output",
|
||||||
|
"update:command",
|
||||||
|
],
|
||||||
computed: {
|
computed: {
|
||||||
saveOutputLocal: {
|
saveOutputLocal: {
|
||||||
get() {
|
get() {
|
||||||
@ -165,6 +170,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
setup() {
|
||||||
|
const addVariable = inject("addVariable");
|
||||||
|
const removeVariable = inject("removeVariable");
|
||||||
|
|
||||||
|
return {
|
||||||
|
addVariable,
|
||||||
|
removeVariable,
|
||||||
|
};
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleClearOutput() {
|
handleClearOutput() {
|
||||||
this.$emit("update:use-output", null);
|
this.$emit("update:use-output", null);
|
||||||
@ -177,6 +191,49 @@ export default defineComponent({
|
|||||||
this.$emit("update:argv", matches[1]);
|
this.$emit("update:argv", matches[1]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleOutputVariableChange(value) {
|
||||||
|
if (this.command.outputVariable) {
|
||||||
|
this.removeVariable(this.command.outputVariable);
|
||||||
|
}
|
||||||
|
if (value) {
|
||||||
|
this.addVariable(value, this.command);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleOutputVariableUpdate(value) {
|
||||||
|
// 创建命令的副本并更新
|
||||||
|
const updatedCommand = {
|
||||||
|
...this.command,
|
||||||
|
outputVariable: value,
|
||||||
|
};
|
||||||
|
// 发出更新事件
|
||||||
|
this.$emit("update:command", updatedCommand);
|
||||||
|
// 处理变量管理
|
||||||
|
this.handleOutputVariableChange(value);
|
||||||
|
},
|
||||||
|
handleArgvTypeUpdate(type) {
|
||||||
|
console.log("Type updated in card:", type);
|
||||||
|
const updatedCommand = {
|
||||||
|
...this.command,
|
||||||
|
argvType: type,
|
||||||
|
};
|
||||||
|
this.$emit("update:command", updatedCommand);
|
||||||
|
},
|
||||||
|
handleToggleOutput() {
|
||||||
|
// 创建命令的副本
|
||||||
|
const updatedCommand = {
|
||||||
|
...this.command,
|
||||||
|
saveOutput: !this.command.saveOutput,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 如果关闭输出,清空变量名
|
||||||
|
if (!updatedCommand.saveOutput && updatedCommand.outputVariable) {
|
||||||
|
this.removeVariable(updatedCommand.outputVariable);
|
||||||
|
updatedCommand.outputVariable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发出更新事件
|
||||||
|
this.$emit("update:command", updatedCommand);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$el.classList.add("composer-card-enter-from");
|
this.$el.classList.add("composer-card-enter-from");
|
||||||
@ -218,8 +275,8 @@ export default defineComponent({
|
|||||||
|
|
||||||
/* 拖拽动画 */
|
/* 拖拽动画 */
|
||||||
/* .composer-card:active { */
|
/* .composer-card:active { */
|
||||||
/* transform: scale(1.02); */
|
/* transform: scale(1.02); */
|
||||||
/* transition: transform 0.2s; */
|
/* transition: transform 0.2s; */
|
||||||
/* } */
|
/* } */
|
||||||
|
|
||||||
.command-item {
|
.command-item {
|
||||||
@ -271,4 +328,105 @@ export default defineComponent({
|
|||||||
.drag-handle:hover {
|
.drag-handle:hover {
|
||||||
color: var(--q-primary);
|
color: var(--q-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 添加新的样式 */
|
||||||
|
.output-section {
|
||||||
|
max-width: 120px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-section :deep(.q-field) {
|
||||||
|
background: rgba(var(--q-primary-rgb), 0.03);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输出按钮样式优化 */
|
||||||
|
.output-btn {
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 4px;
|
||||||
|
min-height: 28px;
|
||||||
|
padding: 0 8px;
|
||||||
|
border: 1px solid rgba(var(--q-primary-rgb), 0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-btn:hover {
|
||||||
|
background: rgba(var(--q-primary-rgb), 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-btn .q-icon {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-btn.q-btn--active {
|
||||||
|
background: rgba(var(--q-primary-rgb), 0.1);
|
||||||
|
color: var(--q-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移除按钮样式 */
|
||||||
|
.remove-btn {
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
color: var(--q-negative);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色模式适配 */
|
||||||
|
.body--dark .output-section :deep(.q-field) {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .output-section :deep(.q-field--focused) {
|
||||||
|
background: #1d1d1d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .output-btn {
|
||||||
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .output-btn:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 输入框内部样式优化 */
|
||||||
|
.output-section :deep(.q-field__control) {
|
||||||
|
height: 28px;
|
||||||
|
min-height: 28px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-section :deep(.q-field__marginal) {
|
||||||
|
height: 28px;
|
||||||
|
width: 24px;
|
||||||
|
min-width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-section :deep(.q-field__native) {
|
||||||
|
padding: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
min-height: 28px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip 样式优化 */
|
||||||
|
:deep(.q-tooltip) {
|
||||||
|
max-width: 300px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优化图标样式 */
|
||||||
|
.output-section :deep(.q-icon) {
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-section :deep(.q-field--focused .q-icon) {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -23,19 +23,20 @@
|
|||||||
class="flow-item"
|
class="flow-item"
|
||||||
:class="{
|
:class="{
|
||||||
'insert-before': dragIndex === index,
|
'insert-before': dragIndex === index,
|
||||||
'insert-after': dragIndex === commands.length && index === commands.length - 1
|
'insert-after':
|
||||||
|
dragIndex === commands.length &&
|
||||||
|
index === commands.length - 1,
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<ComposerCard
|
<ComposerCard
|
||||||
:command="element"
|
:command="element"
|
||||||
:has-output="hasOutput(element)"
|
|
||||||
:can-use-output="canUseOutput(element, index)"
|
|
||||||
:available-outputs="getAvailableOutputs(index)"
|
:available-outputs="getAvailableOutputs(index)"
|
||||||
:placeholder="getPlaceholder(element, index)"
|
:placeholder="getPlaceholder(element, index)"
|
||||||
@remove="removeCommand(index)"
|
@remove="removeCommand(index)"
|
||||||
@toggle-output="toggleSaveOutput(index)"
|
@toggle-output="toggleSaveOutput(index)"
|
||||||
@update:argv="(val) => handleArgvChange(index, val)"
|
@update:argv="(val) => handleArgvChange(index, val)"
|
||||||
@update:use-output="(val) => handleUseOutputChange(index, val)"
|
@update:use-output="(val) => handleUseOutputChange(index, val)"
|
||||||
|
@update:command="(val) => updateCommand(index, val)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
@ -56,7 +57,6 @@
|
|||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import draggable from "vuedraggable";
|
import draggable from "vuedraggable";
|
||||||
import ComposerCard from "./ComposerCard.vue";
|
import ComposerCard from "./ComposerCard.vue";
|
||||||
import { commandsWithOutput, commandsAcceptOutput } from "./composerConfig";
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "ComposerFlow",
|
name: "ComposerFlow",
|
||||||
@ -84,8 +84,8 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
dragIndex: -1,
|
dragIndex: -1,
|
||||||
isDragging: false
|
isDragging: false,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
@ -100,7 +100,7 @@ export default defineComponent({
|
|||||||
onDragOver(event) {
|
onDragOver(event) {
|
||||||
if (!this.isDragging) {
|
if (!this.isDragging) {
|
||||||
const rect = event.currentTarget.getBoundingClientRect();
|
const rect = event.currentTarget.getBoundingClientRect();
|
||||||
const items = this.$el.querySelectorAll('.flow-item');
|
const items = this.$el.querySelectorAll(".flow-item");
|
||||||
const mouseY = event.clientY;
|
const mouseY = event.clientY;
|
||||||
|
|
||||||
// 找到最近的插入位置
|
// 找到最近的插入位置
|
||||||
@ -135,11 +135,11 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onDrop(event) {
|
onDrop(event) {
|
||||||
const actionData = JSON.parse(event.dataTransfer.getData('action'));
|
const actionData = JSON.parse(event.dataTransfer.getData("action"));
|
||||||
const newCommand = {
|
const newCommand = {
|
||||||
...actionData,
|
...actionData,
|
||||||
id: Date.now(), // 或使用其他方式生成唯一ID
|
id: Date.now(), // 或使用其他方式生成唯一ID
|
||||||
argv: '',
|
argv: "",
|
||||||
saveOutput: false,
|
saveOutput: false,
|
||||||
useOutput: null,
|
useOutput: null,
|
||||||
cmd: actionData.value || actionData.cmd,
|
cmd: actionData.value || actionData.cmd,
|
||||||
@ -153,11 +153,11 @@ export default defineComponent({
|
|||||||
newCommands.push(newCommand);
|
newCommands.push(newCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit('update:modelValue', newCommands);
|
this.$emit("update:modelValue", newCommands);
|
||||||
this.dragIndex = -1;
|
this.dragIndex = -1;
|
||||||
|
|
||||||
document.querySelectorAll('.dragging').forEach(el => {
|
document.querySelectorAll(".dragging").forEach((el) => {
|
||||||
el.classList.remove('dragging');
|
el.classList.remove("dragging");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
removeCommand(index) {
|
removeCommand(index) {
|
||||||
@ -165,15 +165,6 @@ export default defineComponent({
|
|||||||
newCommands.splice(index, 1);
|
newCommands.splice(index, 1);
|
||||||
this.$emit("update:modelValue", newCommands);
|
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) {
|
getAvailableOutputs(currentIndex) {
|
||||||
return this.commands
|
return this.commands
|
||||||
.slice(0, currentIndex)
|
.slice(0, currentIndex)
|
||||||
@ -198,7 +189,10 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
handleArgvChange(index, value) {
|
handleArgvChange(index, value) {
|
||||||
const newCommands = [...this.commands];
|
const newCommands = [...this.commands];
|
||||||
newCommands[index].argv = value;
|
newCommands[index] = {
|
||||||
|
...newCommands[index],
|
||||||
|
argv: value,
|
||||||
|
};
|
||||||
this.$emit("update:modelValue", newCommands);
|
this.$emit("update:modelValue", newCommands);
|
||||||
},
|
},
|
||||||
handleUseOutputChange(index, value) {
|
handleUseOutputChange(index, value) {
|
||||||
@ -215,6 +209,15 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
return element.desc;
|
return element.desc;
|
||||||
},
|
},
|
||||||
|
updateCommand(index, updatedCommand) {
|
||||||
|
console.log("Command updated in flow:", updatedCommand);
|
||||||
|
const newCommands = [...this.commands];
|
||||||
|
newCommands[index] = {
|
||||||
|
...newCommands[index],
|
||||||
|
...updatedCommand,
|
||||||
|
};
|
||||||
|
this.$emit("update:modelValue", newCommands);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -227,7 +230,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
.command-flow-container {
|
.command-flow-container {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
background-color: #fafafa;
|
background-color: rgba(255, 255, 255, 0.8);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -237,7 +240,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.body--dark .command-flow-container {
|
.body--dark .command-flow-container {
|
||||||
background-color: #303132;
|
background-color: rgba(32, 32, 32, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.flow-list {
|
.flow-list {
|
||||||
@ -286,7 +289,7 @@ export default defineComponent({
|
|||||||
border-color: #676666;
|
border-color: #676666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 滑动淡出动画 */
|
/* 滑动淡出<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>画 */
|
||||||
.slide-fade-enter-active,
|
.slide-fade-enter-active,
|
||||||
.slide-fade-leave-active {
|
.slide-fade-leave-active {
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
@ -305,7 +308,7 @@ export default defineComponent({
|
|||||||
/* 拖拽指示器基础样式 */
|
/* 拖拽指示器基础样式 */
|
||||||
.flow-item::before,
|
.flow-item::before,
|
||||||
.flow-item::after {
|
.flow-item::after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
@ -338,18 +341,14 @@ export default defineComponent({
|
|||||||
.flow-item.insert-before::before {
|
.flow-item.insert-before::before {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: scaleX(1) translateY(0);
|
transform: scaleX(1) translateY(0);
|
||||||
box-shadow:
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.03), 0 0 4px rgba(0, 0, 0, 0.05);
|
||||||
0 0 10px rgba(0, 0, 0, 0.03),
|
|
||||||
0 0 4px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 激活状态 - 插入到最后 */
|
/* 激活状态 - 插入到最后 */
|
||||||
.flow-item.insert-after::after {
|
.flow-item.insert-after::after {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: scaleX(1) translateY(0);
|
transform: scaleX(1) translateY(0);
|
||||||
box-shadow:
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.03), 0 0 4px rgba(0, 0, 0, 0.05);
|
||||||
0 0 10px rgba(0, 0, 0, 0.03),
|
|
||||||
0 0 4px rgba(0, 0, 0, 0.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 拖拽时的卡片效果 */
|
/* 拖拽时的卡片效果 */
|
||||||
@ -382,8 +381,7 @@ export default defineComponent({
|
|||||||
rgba(255, 255, 255, 0.08) 90%,
|
rgba(255, 255, 255, 0.08) 90%,
|
||||||
transparent
|
transparent
|
||||||
);
|
);
|
||||||
box-shadow:
|
box-shadow: 0 0 10px rgba(255, 255, 255, 0.03),
|
||||||
0 0 10px rgba(255, 255, 255, 0.03),
|
|
||||||
0 0 4px rgba(255, 255, 255, 0.05);
|
0 0 4px rgba(255, 255, 255, 0.05);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="composer-list">
|
<div class="composer-list">
|
||||||
<q-list bordered separator class="rounded-borders">
|
<q-list separator class="rounded-borders">
|
||||||
<template v-for="category in commandCategories" :key="category.label">
|
<template v-for="category in commandCategories" :key="category.label">
|
||||||
<q-item-label header class="q-py-sm">
|
<q-item-label header class="q-py-sm">
|
||||||
<div class="row items-center">
|
<div class="row items-center">
|
||||||
@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { commandCategories } from "./composerConfig";
|
import { commandCategories } from "js/composer/composerConfig";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "ComposerList",
|
name: "ComposerList",
|
||||||
|
448
src/components/editor/composer/LogicFlowCard.vue
Normal file
448
src/components/editor/composer/LogicFlowCard.vue
Normal file
@ -0,0 +1,448 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="logic-flow-card"
|
||||||
|
@dragover.stop.prevent
|
||||||
|
@drop.stop.prevent="onCardDrop"
|
||||||
|
>
|
||||||
|
<q-card class="logic-container">
|
||||||
|
<q-card-section class="q-pa-sm">
|
||||||
|
<!-- 标题栏 -->
|
||||||
|
<div class="row items-center q-mb-sm">
|
||||||
|
<div class="drag-handle cursor-move q-mr-sm">
|
||||||
|
<q-icon name="drag_indicator" size="18px" class="text-grey-6" />
|
||||||
|
</div>
|
||||||
|
<div class="text-subtitle1">{{ command.label }}</div>
|
||||||
|
<q-space />
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="close"
|
||||||
|
@click="$emit('remove')"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 条件输入 -->
|
||||||
|
<q-input
|
||||||
|
v-model="condition"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
label="条件表达式"
|
||||||
|
class="q-mb-md"
|
||||||
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<q-icon name="code" size="18px" />
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<!-- 分支流程 -->
|
||||||
|
<div class="branch-flows q-gutter-y-md">
|
||||||
|
<!-- IF 分支 -->
|
||||||
|
<div class="branch-container">
|
||||||
|
<div class="branch-header q-mb-sm">IF 分支</div>
|
||||||
|
<div
|
||||||
|
class="branch-drop-area"
|
||||||
|
:class="{ 'is-active': isIfBranchActive }"
|
||||||
|
@dragover.stop.prevent="onBranchDragOver('if', $event)"
|
||||||
|
@drop.stop.prevent="onBranchDrop('if', $event)"
|
||||||
|
@dragleave.prevent="onBranchDragLeave('if')"
|
||||||
|
>
|
||||||
|
<!-- 拖拽指示器 -->
|
||||||
|
<div v-if="isIfBranchActive" class="branch-indicator">
|
||||||
|
<q-icon name="add" size="24px" class="text-primary" />
|
||||||
|
<div class="text-caption text-primary q-mt-xs">
|
||||||
|
添加到 IF 分支
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<draggable
|
||||||
|
v-model="ifCommands"
|
||||||
|
group="commands"
|
||||||
|
item-key="id"
|
||||||
|
handle=".drag-handle"
|
||||||
|
:animation="200"
|
||||||
|
>
|
||||||
|
<template #item="{ element, index }">
|
||||||
|
<ComposerCard
|
||||||
|
:command="element"
|
||||||
|
:available-outputs="getAvailableOutputs(index, 'if')"
|
||||||
|
:placeholder="getPlaceholder(element, index, 'if')"
|
||||||
|
@remove="removeCommand('if', index)"
|
||||||
|
@toggle-output="toggleSaveOutput('if', index)"
|
||||||
|
@update:argv="(val) => handleArgvChange('if', index, val)"
|
||||||
|
@update:use-output="
|
||||||
|
(val) => handleUseOutputChange('if', index, val)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ELSE 分支 -->
|
||||||
|
<div class="branch-container">
|
||||||
|
<div class="branch-header q-mb-sm">ELSE 分支</div>
|
||||||
|
<div
|
||||||
|
class="branch-drop-area"
|
||||||
|
:class="{ 'is-active': isElseBranchActive }"
|
||||||
|
@dragover.stop.prevent="onBranchDragOver('else', $event)"
|
||||||
|
@drop.stop.prevent="onBranchDrop('else', $event)"
|
||||||
|
@dragleave.prevent="onBranchDragLeave('else')"
|
||||||
|
>
|
||||||
|
<!-- 拖拽指示器 -->
|
||||||
|
<div v-if="isElseBranchActive" class="branch-indicator">
|
||||||
|
<q-icon name="add" size="24px" class="text-primary" />
|
||||||
|
<div class="text-caption text-primary q-mt-xs">
|
||||||
|
添加到 ELSE 分支
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<draggable
|
||||||
|
v-model="elseCommands"
|
||||||
|
group="commands"
|
||||||
|
item-key="id"
|
||||||
|
handle=".drag-handle"
|
||||||
|
:animation="200"
|
||||||
|
>
|
||||||
|
<template #item="{ element, index }">
|
||||||
|
<ComposerCard
|
||||||
|
:command="element"
|
||||||
|
:available-outputs="getAvailableOutputs(index, 'else')"
|
||||||
|
:placeholder="getPlaceholder(element, index, 'else')"
|
||||||
|
@remove="removeCommand('else', index)"
|
||||||
|
@toggle-output="toggleSaveOutput('else', index)"
|
||||||
|
@update:argv="(val) => handleArgvChange('else', index, val)"
|
||||||
|
@update:use-output="
|
||||||
|
(val) => handleUseOutputChange('else', index, val)
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</draggable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import draggable from "vuedraggable";
|
||||||
|
import ComposerCard from "./ComposerCard.vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "LogicFlowCard",
|
||||||
|
components: {
|
||||||
|
draggable,
|
||||||
|
ComposerCard,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
command: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
condition: "",
|
||||||
|
if: [],
|
||||||
|
else: [],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["update:modelValue", "remove"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isIfBranchActive: false,
|
||||||
|
isElseBranchActive: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
condition: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue.condition;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
condition: value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ifCommands: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue.if;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
if: value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
elseCommands: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue.else;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
else: value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 分支拖拽相关方法
|
||||||
|
onBranchDragOver(branch, event) {
|
||||||
|
// 检查是否有有效的拖拽数据
|
||||||
|
try {
|
||||||
|
const types = event.dataTransfer.types;
|
||||||
|
if (!types.includes("application/json")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.stopPropagation();
|
||||||
|
if (branch === "if") {
|
||||||
|
this.isIfBranchActive = true;
|
||||||
|
} else {
|
||||||
|
this.isElseBranchActive = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onBranchDragLeave(branch) {
|
||||||
|
if (branch === "if") {
|
||||||
|
this.isIfBranchActive = false;
|
||||||
|
} else {
|
||||||
|
this.isElseBranchActive = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onBranchDrop(branch, event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
let actionData;
|
||||||
|
try {
|
||||||
|
const data = event.dataTransfer.getData("application/json");
|
||||||
|
if (!data) {
|
||||||
|
console.warn("No valid drag data found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
actionData = JSON.parse(data);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to parse drag data:", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保有效的命令数据
|
||||||
|
if (!actionData || !actionData.value) {
|
||||||
|
console.warn("Invalid command data");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newCommand = {
|
||||||
|
...actionData,
|
||||||
|
id: Date.now(),
|
||||||
|
argv: "",
|
||||||
|
saveOutput: false,
|
||||||
|
useOutput: null,
|
||||||
|
cmd: actionData.value || actionData.cmd,
|
||||||
|
value: actionData.value || actionData.cmd,
|
||||||
|
};
|
||||||
|
|
||||||
|
const commands =
|
||||||
|
branch === "if" ? [...this.ifCommands] : [...this.elseCommands];
|
||||||
|
commands.push(newCommand);
|
||||||
|
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
[branch]: commands,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重置拖拽状态
|
||||||
|
if (branch === "if") {
|
||||||
|
this.isIfBranchActive = false;
|
||||||
|
} else {
|
||||||
|
this.isElseBranchActive = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除拖拽样式
|
||||||
|
document.querySelectorAll(".dragging").forEach((el) => {
|
||||||
|
el.classList.remove("dragging");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 卡片拖拽方法 - 用于整个逻辑卡片的拖拽
|
||||||
|
onCardDrop(event) {
|
||||||
|
// 不处理,让父组件处理卡片级别的拖拽
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 命令操作方法
|
||||||
|
removeCommand(branch, index) {
|
||||||
|
const commands =
|
||||||
|
branch === "if" ? [...this.ifCommands] : [...this.elseCommands];
|
||||||
|
commands.splice(index, 1);
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
[branch]: commands,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getAvailableOutputs(currentIndex, branch) {
|
||||||
|
// 获取当前分支的命令列表
|
||||||
|
const commands = branch === "if" ? this.ifCommands : this.elseCommands;
|
||||||
|
|
||||||
|
return commands
|
||||||
|
.slice(0, currentIndex)
|
||||||
|
.map((cmd, index) => ({
|
||||||
|
label: `${cmd.label} 的输出`,
|
||||||
|
value: index,
|
||||||
|
disable: !cmd.saveOutput,
|
||||||
|
}))
|
||||||
|
.filter((item) => !item.disable);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggleSaveOutput(branch, index) {
|
||||||
|
const commands =
|
||||||
|
branch === "if" ? [...this.ifCommands] : [...this.elseCommands];
|
||||||
|
commands[index].saveOutput = !commands[index].saveOutput;
|
||||||
|
|
||||||
|
// 如果取消保存输出,清除所有使用此输出的命令
|
||||||
|
if (!commands[index].saveOutput) {
|
||||||
|
commands.forEach((cmd, i) => {
|
||||||
|
if (i > index && cmd.useOutput === index) {
|
||||||
|
cmd.useOutput = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
[branch]: commands,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleArgvChange(branch, index, value) {
|
||||||
|
const commands =
|
||||||
|
branch === "if" ? [...this.ifCommands] : [...this.elseCommands];
|
||||||
|
commands[index].argv = value;
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
[branch]: commands,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleUseOutputChange(branch, index, value) {
|
||||||
|
const commands =
|
||||||
|
branch === "if" ? [...this.ifCommands] : [...this.elseCommands];
|
||||||
|
commands[index].useOutput = value;
|
||||||
|
if (value !== null) {
|
||||||
|
commands[index].argv = "";
|
||||||
|
}
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
[branch]: commands,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getPlaceholder(element, index, branch) {
|
||||||
|
if (element.useOutput !== null) {
|
||||||
|
const commands = branch === "if" ? this.ifCommands : this.elseCommands;
|
||||||
|
return `使用 ${commands[element.useOutput].label} 的输出`;
|
||||||
|
}
|
||||||
|
return element.desc;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.logic-flow-card {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logic-container {
|
||||||
|
background: rgba(255, 255, 255, 0.8);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-container {
|
||||||
|
background: rgba(0, 0, 0, 0.02);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-header {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--q-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-drop-area {
|
||||||
|
min-height: 50px;
|
||||||
|
border: 1px dashed rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
padding: 4px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-drop-area.is-active {
|
||||||
|
border-color: var(--q-primary);
|
||||||
|
background: rgba(var(--q-primary-rgb), 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 拖拽指示器样式 */
|
||||||
|
.branch-indicator {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 1;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
animation: indicator-fade-in 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes indicator-fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translate(-50%, -40%);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .logic-container {
|
||||||
|
background: rgba(34, 34, 34, 0.3);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .branch-container {
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .branch-drop-area {
|
||||||
|
border-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .branch-indicator {
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
</style>
|
255
src/components/editor/composer/VariableInput.vue
Normal file
255
src/components/editor/composer/VariableInput.vue
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
<template>
|
||||||
|
<q-input
|
||||||
|
v-model="inputValue"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
:label="label"
|
||||||
|
class="variable-input"
|
||||||
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
round
|
||||||
|
:icon="isString ? 'format_quote' : 'format_quote'"
|
||||||
|
size="sm"
|
||||||
|
:class="{
|
||||||
|
'text-primary': isString,
|
||||||
|
'text-grey-6': !isString,
|
||||||
|
}"
|
||||||
|
class="string-toggle"
|
||||||
|
@click="toggleStringType"
|
||||||
|
>
|
||||||
|
<q-tooltip>{{
|
||||||
|
isString
|
||||||
|
? "当前类型是:字符串,点击切换"
|
||||||
|
: "当前类型是:变量、数字、表达式等,点击切换"
|
||||||
|
}}</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:append>
|
||||||
|
<q-btn
|
||||||
|
v-if="hasSelectedVariable"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
round
|
||||||
|
icon="close"
|
||||||
|
size="sm"
|
||||||
|
class="clear-btn q-mr-xs"
|
||||||
|
@click="clearVariable"
|
||||||
|
>
|
||||||
|
<q-tooltip>清除选中的变量</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-btn-dropdown
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
:icon="hasSelectedVariable ? 'data_object' : 'functions'"
|
||||||
|
:class="{
|
||||||
|
'text-primary': hasSelectedVariable,
|
||||||
|
'text-grey-6': !hasSelectedVariable,
|
||||||
|
}"
|
||||||
|
class="variable-dropdown"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
<q-list class="variable-list">
|
||||||
|
<q-item-label header class="text-subtitle2">
|
||||||
|
<q-icon name="functions" size="16px" class="q-mr-sm" />
|
||||||
|
选择变量
|
||||||
|
</q-item-label>
|
||||||
|
|
||||||
|
<q-separator v-if="variables.length > 0" />
|
||||||
|
|
||||||
|
<template v-if="variables.length > 0">
|
||||||
|
<q-item
|
||||||
|
v-for="variable in variables"
|
||||||
|
:key="variable.name"
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="insertVariable(variable)"
|
||||||
|
class="variable-item"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label class="variable-name">
|
||||||
|
{{ variable.name }}
|
||||||
|
</q-item-label>
|
||||||
|
<q-item-label caption class="variable-source">
|
||||||
|
来自: {{ variable.sourceCommand.label }}
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<q-item v-else class="text-grey-6">
|
||||||
|
<q-item-section class="text-center">
|
||||||
|
<q-item-label>暂无可用变量</q-item-label>
|
||||||
|
<q-item-label caption>
|
||||||
|
点击命令卡片的「获取输出」按钮输入保存的变量名
|
||||||
|
</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</q-list>
|
||||||
|
</q-btn-dropdown>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent, inject, computed } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "VariableInput",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: String,
|
||||||
|
label: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ["update:modelValue", "update:type"],
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const variables = inject("composerVariables", []);
|
||||||
|
return { variables };
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isString: true,
|
||||||
|
selectedVariable: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
inputValue: {
|
||||||
|
get() {
|
||||||
|
return this.modelValue;
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit("update:modelValue", value);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hasSelectedVariable() {
|
||||||
|
return this.selectedVariable !== null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleStringType() {
|
||||||
|
if (!this.hasSelectedVariable) {
|
||||||
|
this.isString = !this.isString;
|
||||||
|
this.$emit("update:type", this.isString ? "string" : "number");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
insertVariable(variable) {
|
||||||
|
this.selectedVariable = variable;
|
||||||
|
this.isString = false;
|
||||||
|
this.$emit("update:type", "variable");
|
||||||
|
this.$emit("update:modelValue", variable.name);
|
||||||
|
},
|
||||||
|
|
||||||
|
clearVariable() {
|
||||||
|
this.selectedVariable = null;
|
||||||
|
this.isString = true;
|
||||||
|
this.$emit("update:type", "string");
|
||||||
|
this.$emit("update:modelValue", "");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
modelValue(newVal) {
|
||||||
|
if (this.selectedVariable && newVal !== this.selectedVariable.name) {
|
||||||
|
this.selectedVariable = null;
|
||||||
|
this.isString = true;
|
||||||
|
this.$emit("update:type", "string");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$emit("update:type", "string");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.variable-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-input :deep(.q-field__control) {
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 字符串切换按钮样式 */
|
||||||
|
.string-toggle {
|
||||||
|
min-width: 24px;
|
||||||
|
padding: 4px;
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.string-toggle:hover {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 变量下拉框样式 */
|
||||||
|
.variable-dropdown {
|
||||||
|
min-width: 32px;
|
||||||
|
padding: 4px;
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-dropdown:hover {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 变量列表样式 */
|
||||||
|
.variable-list {
|
||||||
|
min-width: 200px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-item {
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 2px 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-item:hover {
|
||||||
|
background: rgba(var(--q-primary-rgb), 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-name {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.variable-source {
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色模式适配 */
|
||||||
|
.body--dark .variable-item:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 清空按钮样式 */
|
||||||
|
.clear-btn {
|
||||||
|
opacity: 0.6;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-btn:hover {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.1);
|
||||||
|
color: var(--q-negative);
|
||||||
|
}
|
||||||
|
</style>
|
@ -66,8 +66,8 @@ import { defineComponent } from "vue";
|
|||||||
import UBrowserBasic from "./UBrowserBasic.vue";
|
import UBrowserBasic from "./UBrowserBasic.vue";
|
||||||
import UBrowserOperations from "./UBrowserOperations.vue";
|
import UBrowserOperations from "./UBrowserOperations.vue";
|
||||||
import UBrowserRun from "./UBrowserRun.vue";
|
import UBrowserRun from "./UBrowserRun.vue";
|
||||||
import { defaultUBrowserConfigs } from "./ubrowserConfig";
|
import { defaultUBrowserConfigs } from "js/composer/ubrowserConfig";
|
||||||
import { generateUBrowserCode } from "./generateUBrowserCode";
|
import { generateUBrowserCode } from "js/composer/generateUBrowserCode";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "UBrowserEditor",
|
name: "UBrowserEditor",
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { ubrowserOperationConfigs } from "../composerConfig";
|
import { ubrowserOperationConfigs } from "js/composer/composerConfig";
|
||||||
import UBrowserOperation from "./operations/UBrowserOperation.vue";
|
import UBrowserOperation from "./operations/UBrowserOperation.vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
export {
|
export {
|
||||||
ubrowserOperationConfigs,
|
ubrowserOperationConfigs,
|
||||||
defaultUBrowserConfigs,
|
defaultUBrowserConfigs,
|
||||||
} from "./ubrowser/ubrowserConfig";
|
} from "./ubrowserConfig";
|
||||||
|
|
||||||
// 定义命令图标映射
|
// 定义命令图标映射
|
||||||
export const commandIcons = {
|
export const commandIcons = {
|
||||||
@ -125,20 +125,3 @@ export const commandCategories = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// 定义哪些命令可以产生输出
|
|
||||||
export const commandsWithOutput = {
|
|
||||||
system: true,
|
|
||||||
open: true,
|
|
||||||
locate: true,
|
|
||||||
copyTo: true,
|
|
||||||
ubrowser: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 定义哪些命令可以接收输出
|
|
||||||
export const commandsAcceptOutput = {
|
|
||||||
message: true,
|
|
||||||
alert: true,
|
|
||||||
send: true,
|
|
||||||
copyTo: true,
|
|
||||||
};
|
|
@ -4,7 +4,7 @@
|
|||||||
* @param {Array} selectedActions 已选择的操作列表
|
* @param {Array} selectedActions 已选择的操作列表
|
||||||
* @returns {string} 生成的代码
|
* @returns {string} 生成的代码
|
||||||
*/
|
*/
|
||||||
import { defaultUBrowserConfigs } from "./ubrowserConfig";
|
import { defaultUBrowserConfigs } from "js/composer/ubrowserConfig";
|
||||||
|
|
||||||
export function generateUBrowserCode(configs, selectedActions) {
|
export function generateUBrowserCode(configs, selectedActions) {
|
||||||
let code = "utools.ubrowser";
|
let code = "utools.ubrowser";
|
Loading…
x
Reference in New Issue
Block a user