使用通用组件构造控制流程命令,规范配置文件字段命名

This commit is contained in:
fofolee 2025-01-09 10:11:39 +08:00
parent b676801b9a
commit f50bad59ab
22 changed files with 612 additions and 1940 deletions

View File

@ -23,8 +23,7 @@
>
<!-- 控制流程组件直接把组件放在head中 -->
<template v-if="localCommand.isControlFlow">
<component
:is="localCommand.component"
<ControlCommand
v-model="localCommand"
v-bind="localCommand.componentProps || {}"
class="control-component"
@ -56,7 +55,7 @@
v-else
v-model="localCommand"
:class="
localCommand.config?.length || localCommand.functionSelector
localCommand.config?.length || localCommand.subCommands
? 'col q-mt-md'
: 'col'
"
@ -75,6 +74,7 @@ import MultiParams from "components/composer/MultiParams.vue";
import CommandHead from "components/composer/card/CommandHead.vue";
import * as CardComponents from "js/composer/cardComponents";
import { processVariable } from "js/composer/variableManager";
import ControlCommand from "components/composer/control/ControlCommand.vue";
export default defineComponent({
name: "ComposerCard",
@ -82,6 +82,7 @@ export default defineComponent({
VariableInput,
MultiParams,
CommandHead,
ControlCommand,
...CardComponents,
},
props: {

View File

@ -1,10 +1,10 @@
<template>
<div class="multi-params">
<OperationCard
v-if="hasFunctionSelector"
v-if="hasSubCommands"
:model-value="funcName"
@update:model-value="funcName = $event"
:options="localCommand.functionSelector"
:options="localCommand.subCommands"
/>
<ParamInput :configs="localConfig" :values="argvs" @update="updateArgv" />
</div>
@ -91,13 +91,13 @@ export default defineComponent({
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
hasFunctionSelector() {
return !!this.localCommand.functionSelector;
hasSubCommands() {
return !!this.localCommand.subCommands;
},
},
methods: {
getSelectFunction(funcName = this.funcName) {
return this.modelValue.functionSelector?.find(
return this.modelValue.subCommands?.find(
(item) => item.value === funcName
);
},

View File

@ -129,7 +129,8 @@ export default {
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 4px;
gap: 8px;
margin-right: 8px;
}
.command-label:hover {

View File

@ -6,8 +6,16 @@
class="grid-item"
:style="getColumnStyle(config.width)"
>
<ControlInput
v-if="config.type === 'controlInput'"
:model-value="values[index]"
@update:model-value="$emit('update', index, $event)"
:label="config.label"
:placeholder="config.placeholder"
:icon="config.icon"
/>
<VariableInput
v-if="config.type === 'varInput'"
v-else-if="config.type === 'varInput'"
:model-value="values[index]"
@update:model-value="$emit('update', index, $event)"
:label="config.label"
@ -98,6 +106,7 @@ import NumberInput from "./NumberInput.vue";
import ArrayEditor from "./ArrayEditor.vue";
import DictEditor from "./DictEditor.vue";
import ButtonGroup from "./ButtonGroup.vue";
import ControlInput from "./ControlInput.vue";
/**
* 参数输入组件
@ -118,6 +127,7 @@ export default defineComponent({
ArrayEditor,
DictEditor,
ButtonGroup,
ControlInput,
},
props: {
configs: {

View File

@ -1,211 +0,0 @@
<template>
<div class="conditional-judgment-wrapper">
<div class="conditional-judgment">
<!-- 类型标签 -->
<div class="control-type-label">
<template v-if="type === 'if'">如果</template>
<template v-else-if="type === 'else'"> 否则 </template>
<template v-else>结束</template>
</div>
<!-- 条件输入区域 -->
<div class="condition-settings">
<template v-if="showCondition">
<ControlInput
:model-value="argvs.condition"
@update:model-value="updateArgvs('condition', $event)"
label="条件"
placeholder="表达式"
class="condition-input"
/>
</template>
</div>
<!-- if类型显示添加按钮 -->
<q-btn
v-if="type === 'if'"
flat
dense
icon="add"
size="sm"
class="control-btn"
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'else',
value: this.modelValue.value,
})
"
>
<q-tooltip>添加条件分支</q-tooltip>
</q-btn>
<!-- mid类型显示切换按钮 -->
<q-btn
v-if="type === 'else'"
flat
dense
size="sm"
:icon="argvs.showMidCondition ? 'remove' : 'add'"
class="control-btn"
@click="toggleCondition"
>
<q-tooltip>{{
argvs.showMidCondition ? "隐藏条件" : "显示条件"
}}</q-tooltip>
</q-btn>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ControlInput from "components/composer/common/ControlInput.vue";
export default defineComponent({
name: "ConditionalJudgment",
components: {
ControlInput,
},
props: {
modelValue: Object,
},
emits: ["update:modelValue", "addBranch"],
data() {
return {
defaultArgvs: {
condition: "",
showMidCondition: false,
},
};
},
computed: {
type() {
return this.modelValue.commandType;
},
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
showCondition() {
return (
this.type === "if" ||
(this.type === "else" && this.argvs.showMidCondition)
);
},
},
methods: {
toggleCondition() {
this.argvs.showMidCondition = !this.argvs.showMidCondition;
if (this.argvs.showMidCondition === false) this.argvs.condition = "";
this.updateModelValue(this.argvs);
},
updateArgvs(key, value) {
const argvs = { ...this.argvs, [key]: value };
this.updateModelValue(argvs);
},
generateCode(argvs) {
switch (this.type) {
case "if":
return `if(${argvs.condition || "true"}){`;
case "else":
return argvs.showMidCondition && argvs.condition
? `}else if(${argvs.condition}){`
: "}else{";
case "end":
return "}";
default:
return "";
}
},
parseCodeToArgvs(code) {
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
if (!code) return argvs;
switch (this.type) {
case "if":
const ifMatch = code.match(/^if\((.*?)\)\{$/);
if (ifMatch) {
argvs.condition = ifMatch[1] === "true" ? "" : ifMatch[1];
}
break;
case "else":
const elseIfMatch = code.match(/^}else if\((.*?)\)\{$/);
if (elseIfMatch) {
argvs.condition = elseIfMatch[1];
argvs.showMidCondition = true;
}
break;
}
return argvs;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
argvs,
code: this.generateCode(argvs),
});
},
},
mounted() {
if (!this.modelValue.argvs && !this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
});
</script>
<style scoped>
.conditional-judgment-wrapper {
width: 100%;
display: flex;
}
.conditional-judgment {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.control-type-label {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0.9;
user-select: none;
}
.condition-settings {
display: flex;
gap: 4px;
flex: 1;
min-width: 0;
}
.condition-input {
flex: 1;
min-width: 0;
}
.control-btn {
width: 21px;
height: 21px;
opacity: 0.7;
flex-shrink: 0;
}
.control-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.body--dark .control-btn {
color: rgba(255, 255, 255, 0.7);
}
.body--dark .control-btn:hover {
color: var(--q-primary);
}
</style>

View File

@ -0,0 +1,262 @@
<template>
<div class="control-command-wrapper">
<div class="control-command">
<!-- 类型标签 -->
<div class="control-type-label">
{{ currentFunction?.label || modelValue.commandType }}
</div>
<!-- 输入区域 -->
<div v-if="currentFunction?.config" class="control-settings">
<ParamInput
:configs="currentFunction.config"
:values="values"
@update="handleInputUpdate"
/>
</div>
<!-- 操作按钮 -->
<q-btn-dropdown
v-if="showBranchButton"
flat
dense
dropdown-icon="add"
size="sm"
no-icon-animation
class="control-btn"
>
<q-list>
<q-item
v-for="func in branchOptions"
:key="func.value"
v-close-popup
clickable
@click="addBranch(func.value)"
class="row items-center no-wrap q-py-none"
>
<q-icon :name="func.icon" class="q-mr-md" />
<q-item-label>{{ func.label }}</q-item-label>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ParamInput from "components/composer/common/ParamInput.vue";
export default defineComponent({
name: "ControlCommand",
components: {
ParamInput,
},
props: {
modelValue: {
type: Object,
required: true,
},
},
emits: ["update:modelValue", "addBranch"],
data() {
return {
defaultArgvs: {},
};
},
computed: {
currentFunction() {
return this.modelValue.subCommands?.find(
(f) => f.value === this.modelValue.commandType
);
},
values() {
if (!this.currentFunction?.config) return [];
return this.currentFunction.config.map(
(input) => this.argvs[input.key] || ""
);
},
showBranchButton() {
return (
this.modelValue.subCommands?.length > 2 &&
this.modelValue.subCommands?.[0]?.value === this.modelValue.commandType
);
},
branchOptions() {
return this.modelValue.subCommands?.slice(1, -1);
},
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
},
methods: {
handleInputUpdate(index, value) {
const input = this.currentFunction.config[index];
this.updateArgvs(input.key, value);
},
updateArgvs(key, value) {
const argvs = {
...this.argvs,
[key]: value,
};
this.updateModelValue(argvs);
},
addBranch(type) {
this.$emit("addBranch", {
chainId: this.modelValue.chainId,
commandType: type,
value: this.modelValue.value,
});
this.updateModelValue(this.argvs || {});
},
generateCode(argvs) {
if (!this.currentFunction?.codeTemplate) return "";
let code = this.currentFunction.codeTemplate;
const variablePattern = /\${(\w+)}/g;
code = code.replace(variablePattern, (_, key) => {
return argvs[key] || "";
});
return code;
},
parseCodeToArgvs(code) {
if (!code) return this.defaultArgvs;
if (!this.currentFunction?.codeTemplate) return {};
const template = this.currentFunction.codeTemplate;
const argvs = {};
//
if (!template.includes("${")) return {};
//
const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
let pattern = escapeRegExp(template);
//
const variables = [];
const variablePattern = /\${(\w+)}/g;
let match;
while ((match = variablePattern.exec(template)) !== null) {
variables.push(match[1]);
pattern = pattern.replace(escapeRegExp(`\${${match[1]}}`), "([^}]*?)");
}
//
const regex = new RegExp(`^${pattern}$`);
const matches = code.match(regex);
if (matches) {
//
variables.forEach((variable, index) => {
argvs[variable] = matches[index + 1] || "";
});
return argvs;
}
return this.defaultArgvs;
},
updateModelValue(argvs) {
const code = this.generateCode(argvs);
this.$emit("update:modelValue", {
...this.modelValue,
argvs,
code,
});
},
},
created() {
//
if (this.currentFunction?.config) {
this.currentFunction.config.forEach((config) => {
this.defaultArgvs[config.key] = config.defaultValue || "";
});
}
},
mounted() {
if (!this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
});
</script>
<style scoped>
.control-command-wrapper {
width: 100%;
display: flex;
}
.control-command {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.control-type-label {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0.9;
user-select: none;
flex-shrink: 0;
}
.control-settings {
display: flex;
flex: 1;
min-width: 0;
margin-right: auto;
}
/* ParamInput 容器样式 */
.control-settings :deep(.param-grid) {
display: flex;
flex-wrap: wrap;
gap: var(--grid-gap);
width: 100%;
--grid-gap: 8px;
}
/* 输入项容器样式 */
.control-settings :deep(.grid-item) {
min-width: 50px;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
/* 输入框样式 */
.control-settings :deep(.grid-item > *) {
flex: 1;
min-width: 0;
}
.control-btn {
opacity: 0.7;
flex-shrink: 0;
margin-left: auto;
}
.control-btn:hover {
opacity: 1;
transform: scale(1.1);
}
/* 暗色模式适配 */
.body--dark .control-btn {
color: rgba(255, 255, 255, 0.7);
}
.body--dark .control-btn:hover {
color: var(--q-primary);
}
</style>

View File

@ -1,226 +0,0 @@
<template>
<div class="loop-control-wrapper" v-bind="$attrs">
<div class="loop-control row items-center no-wrap">
<!-- 类型标签和按钮区域 -->
<div class="control-type-label">
<template v-if="type === 'forEach'">开始</template>
<template v-else-if="type === 'continue'">继续</template>
<template v-else-if="type === 'break'">终止</template>
<template v-else>结束</template>
</div>
<!-- 遍历设置区域 -->
<div v-if="type === 'forEach'" class="loop-settings">
<ControlInput
:model-value="argvs.itemVar"
@update:model-value="updateArgvs('itemVar', $event)"
label="元素"
:is-variable="true"
class="loop-input"
/>
<ControlInput
:model-value="argvs.indexVar"
@update:model-value="updateArgvs('indexVar', $event)"
label="索引"
:is-variable="true"
class="loop-input"
/>
<ControlInput
:model-value="argvs.arrayVar"
@update:model-value="updateArgvs('arrayVar', $event)"
label="数组"
class="loop-input array-input"
/>
</div>
<!-- 只在循环开始时显示添加按钮 -->
<q-btn-dropdown
v-if="type === 'forEach'"
flat
dense
dropdown-icon="add"
no-icon-animation
size="sm"
class="control-btn"
>
<q-list>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'continue',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加继续循环</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'break',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加终止循环</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ControlInput from "components/composer/common/ControlInput.vue";
export default defineComponent({
name: "ForEachControl",
components: {
ControlInput,
},
inheritAttrs: false,
props: {
modelValue: Object,
},
emits: ["update:modelValue", "addBranch"],
data() {
return {
defaultArgvs: {
itemVar: "item",
indexVar: "index",
arrayVar: "array",
},
};
},
computed: {
type() {
return this.modelValue.commandType;
},
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
},
methods: {
updateArgvs(key, value) {
const argvs = { ...this.argvs, [key]: value };
this.updateModelValue(argvs);
},
generateCode(argvs = this.argvs) {
switch (this.type) {
case "forEach":
const item = argvs.itemVar || "item";
const index = argvs.indexVar || "index";
const array = argvs.arrayVar || "array";
return `for(let [${index}, ${item}] of ${array}.entries()){`;
case "continue":
return "continue;";
case "break":
return "break;";
case "end":
return "}";
default:
return "";
}
},
parseCodeToArgvs(code) {
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
if (!code) return argvs;
if (this.type === "forEach") {
const match = code.match(
/^(\w+)\.forEach\(\((\w+),\s*(\w+)\)\s*=>\s*\{$/
);
if (match) {
argvs.arrayVar = match[1];
argvs.itemVar = match[2];
argvs.indexVar = match[3];
}
}
return argvs;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
argvs,
code: this.generateCode(argvs),
});
},
},
mounted() {
if (!this.modelValue.argvs && !this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
});
</script>
<style scoped>
.loop-control-wrapper {
width: 100%;
display: flex;
}
.loop-control {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.control-type-label {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0.9;
user-select: none;
}
.control-btn {
width: 21px;
height: 21px;
opacity: 0.7;
flex-shrink: 0;
}
.control-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.loop-settings {
display: flex;
gap: 4px;
flex: 1;
min-width: 0;
}
.loop-input {
width: 80px;
}
.array-input {
width: 120px;
}
/* 暗色模式适配 */
.body--dark .control-btn {
color: rgba(255, 255, 255, 0.7);
}
.body--dark .control-btn:hover {
color: var(--q-primary);
}
</style>

View File

@ -1,223 +0,0 @@
<template>
<div class="loop-control-wrapper" v-bind="$attrs">
<div class="loop-control row items-center no-wrap">
<!-- 类型标签和按钮区域 -->
<div class="control-type-label">
<template v-if="type === 'forIn'">开始</template>
<template v-else-if="type === 'continue'">继续</template>
<template v-else-if="type === 'break'">终止</template>
<template v-else>结束</template>
</div>
<!-- 遍历设置区域 -->
<div v-if="type === 'forIn'" class="loop-settings">
<ControlInput
:model-value="argvs.keyVar"
@update:model-value="updateArgvs('keyVar', $event)"
label="键名"
:is-variable="true"
class="loop-input"
/>
<ControlInput
:model-value="argvs.valueVar"
@update:model-value="updateArgvs('valueVar', $event)"
label="值"
:is-variable="true"
class="loop-input"
/>
<ControlInput
:model-value="argvs.objectVar"
@update:model-value="updateArgvs('objectVar', $event)"
label="对象"
class="loop-input object-input"
/>
</div>
<!-- 只在循环开始时显示添加按钮 -->
<q-btn-dropdown
v-if="type === 'forIn'"
flat
dense
dropdown-icon="add"
no-icon-animation
size="sm"
class="control-btn"
>
<q-list>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'continue',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加继续循环</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'break',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加终止循环</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ControlInput from "components/composer/common/ControlInput.vue";
export default defineComponent({
name: "ForInControl",
components: {
ControlInput,
},
inheritAttrs: false,
props: {
modelValue: Object,
},
data() {
return {
defaultArgvs: {
keyVar: "key",
valueVar: "value",
objectVar: "obj",
},
};
},
emits: ["update:modelValue", "addBranch"],
computed: {
type() {
return this.modelValue.commandType;
},
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
},
methods: {
updateArgvs(key, value) {
const argvs = { ...this.argvs, [key]: value };
this.updateModelValue(argvs);
},
generateCode(argvs = this.argvs) {
switch (this.type) {
case "forIn":
const key = argvs.keyVar || "key";
const value = argvs.valueVar || "value";
const obj = argvs.objectVar || "obj";
return `for(const ${key} in ${obj}){const ${value}=${obj}[${key}];`;
case "continue":
return "continue;";
case "break":
return "break;";
case "end":
return "}";
default:
return "";
}
},
parseCodeToArgvs(code) {
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
if (!code) return argvs;
if (this.type === "forIn") {
const match = code.match(/^for\(const\s+(\w+)\s+in\s+(\w+)\)\{$/);
if (match) {
argvs.keyVar = match[1];
argvs.objectVar = match[2];
}
}
return argvs;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
argvs,
code: this.generateCode(argvs),
});
},
},
mounted() {
if (!this.modelValue.argvs && !this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
});
</script>
<style scoped>
.loop-control-wrapper {
width: 100%;
display: flex;
}
.loop-control {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.control-type-label {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0.9;
user-select: none;
}
.control-btn {
width: 21px;
height: 21px;
opacity: 0.7;
flex-shrink: 0;
}
.control-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.loop-settings {
display: flex;
gap: 4px;
flex: 1;
min-width: 0;
}
.loop-input {
width: 80px;
}
.object-input {
width: 120px;
}
/* 暗色模式适配 */
.body--dark .control-btn {
color: rgba(255, 255, 255, 0.7);
}
.body--dark .control-btn:hover {
color: var(--q-primary);
}
</style>

View File

@ -1,230 +0,0 @@
<template>
<div class="loop-control-wrapper" v-bind="$attrs">
<div class="loop-control row items-center no-wrap">
<!-- 类型标签和按钮区域 -->
<div class="control-type-label">
<template v-if="type === 'loop'">开始</template>
<template v-else-if="type === 'continue'">继续</template>
<template v-else-if="type === 'break'">终止</template>
<template v-else>结束</template>
</div>
<!-- 循环设置区域 -->
<div v-if="type === 'loop'" class="loop-settings">
<ControlInput
:model-value="argvs.indexVar"
@update:model-value="updateArgvs('indexVar', $event)"
label="变量"
:is-variable="true"
class="loop-input"
/>
<ControlInput
:model-value="argvs.startValue"
@update:model-value="updateArgvs('startValue', $event)"
label="从"
class="loop-input"
/>
<ControlInput
:model-value="argvs.endValue"
@update:model-value="updateArgvs('endValue', $event)"
label="到"
class="loop-input"
/>
<ControlInput
:model-value="argvs.stepValue"
@update:model-value="updateArgvs('stepValue', $event)"
label="步进"
class="loop-input"
/>
</div>
<!-- 只在循环开始时显示添加按钮 -->
<q-btn-dropdown
v-if="type === 'loop'"
flat
dense
dropdown-icon="add"
no-icon-animation
size="sm"
class="control-btn"
>
<q-list>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'continue',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加继续循环</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'break',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加终止循环</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ControlInput from "components/composer/common/ControlInput.vue";
export default defineComponent({
name: "LoopControl",
components: {
ControlInput,
},
inheritAttrs: false,
props: {
modelValue: Object,
},
emits: ["update:modelValue", "addBranch"],
data() {
return {
defaultArgvs: {
indexVar: "i",
startValue: 0,
endValue: 10,
stepValue: 1,
},
};
},
computed: {
type() {
return this.modelValue.commandType;
},
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
},
methods: {
updateArgvs(key, value) {
const argvs = { ...this.argvs, [key]: value };
this.updateModelValue(argvs);
},
generateCode(argvs = this.argvs) {
switch (this.type) {
case "loop":
const index = argvs.indexVar || "i";
const start = argvs.startValue || 0;
const end = argvs.endValue || 10;
const step = argvs.stepValue || 1;
return `for(let ${index}=${start};${index}<${end};${index}+=${step}){`;
case "continue":
return "continue;";
case "break":
return "break;";
case "end":
return "}";
default:
return "";
}
},
parseCodeToArgvs(code) {
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
if (!code) return argvs;
if (this.type === "loop") {
const match = code.match(
/^for\(let\s+(\w+)=(\d+);.*?<(\d+);.*?\+=(\d+)\)\{$/
);
if (match) {
argvs.indexVar = match[1];
argvs.startValue = parseInt(match[2]);
argvs.endValue = parseInt(match[3]);
argvs.stepValue = parseInt(match[4]);
}
}
return argvs;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
argvs,
code: this.generateCode(argvs),
});
},
},
mounted() {
if (!this.modelValue.argvs && !this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
});
</script>
<style scoped>
.loop-control-wrapper {
width: 100%;
display: flex;
}
.loop-control {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.control-type-label {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0.9;
user-select: none;
}
.control-btn {
width: 21px;
height: 21px;
opacity: 0.7;
flex-shrink: 0;
}
.control-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.loop-settings {
display: flex;
gap: 4px;
flex: 1;
min-width: 0;
}
.loop-input {
width: 80px;
}
/* 暗色模式适配 */
.body--dark .control-btn {
color: rgba(255, 255, 255, 0.7);
}
.body--dark .control-btn:hover {
color: var(--q-primary);
}
</style>

View File

@ -1,219 +0,0 @@
<template>
<div class="switch-control-wrapper">
<div class="switch-control">
<!-- 类型标签 -->
<div class="control-type-label">
<template v-if="type === 'switch'">选择</template>
<template v-else-if="type === 'case'">匹配</template>
<template v-else-if="type === 'default'">默认</template>
<template v-else>结束</template>
</div>
<!-- 条件输入区域 -->
<div class="switch-settings">
<template v-if="type === 'switch'">
<ControlInput
:model-value="argvs.expression"
@update:model-value="updateArgvs('expression', $event)"
label="变量"
placeholder="变量或表达式"
class="switch-input"
/>
</template>
<template v-else-if="type === 'case'">
<ControlInput
:model-value="argvs.value"
@update:model-value="updateArgvs('value', $event)"
label="值"
placeholder="匹配值"
class="switch-input"
/>
</template>
</div>
<!-- 只在switch开始时显示添加按钮 -->
<q-btn-dropdown
v-if="type === 'switch'"
flat
dense
dropdown-icon="add"
no-icon-animation
size="sm"
class="control-btn"
>
<q-list>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'case',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加匹配分支</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'default',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加默认分支</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ControlInput from "components/composer/common/ControlInput.vue";
export default defineComponent({
name: "SwitchControl",
components: {
ControlInput,
},
inheritAttrs: false,
props: {
modelValue: Object,
},
data() {
return {
defaultArgvs: {
expression: "",
value: "",
},
};
},
emits: ["update:modelValue", "addBranch"],
computed: {
type() {
return this.modelValue.commandType;
},
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
},
methods: {
updateArgvs(key, value) {
const argvs = { ...this.argvs, [key]: value };
this.updateModelValue(argvs);
},
generateCode(argvs = this.argvs) {
switch (this.type) {
case "switch":
return `switch(${argvs.expression || "value"}){`;
case "case":
return `case ${argvs.value}:`;
case "default":
return "default:";
case "end":
return "}";
}
},
parseCodeToArgvs(code) {
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
if (!code) return argvs;
switch (this.type) {
case "switch":
const switchMatch = code.match(/^switch\((.*?)\)\{$/);
if (switchMatch) {
argvs.expression = switchMatch[1] === "value" ? "" : switchMatch[1];
}
break;
case "case":
const caseMatch = code.match(/^case\s+(.*?):$/);
if (caseMatch) {
argvs.value = caseMatch[1];
}
break;
}
return argvs;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
argvs,
code: this.generateCode(argvs),
});
},
},
mounted() {
if (!this.modelValue.argvs && !this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
});
</script>
<style scoped>
.switch-control-wrapper {
width: 100%;
display: flex;
}
.switch-control {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.control-type-label {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0.9;
user-select: none;
}
.control-btn {
width: 21px;
height: 21px;
opacity: 0.7;
flex-shrink: 0;
}
.control-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.switch-settings {
display: flex;
gap: 4px;
flex: 1;
min-width: 0;
}
.switch-input {
flex: 1;
min-width: 0;
}
/* 暗色模式适配 */
.body--dark .control-btn {
color: rgba(255, 255, 255, 0.7);
}
.body--dark .control-btn:hover {
color: var(--q-primary);
}
</style>

View File

@ -1,203 +0,0 @@
<template>
<div class="try-catch-wrapper">
<div class="try-catch">
<!-- 类型标签 -->
<div class="control-type-label">
<template v-if="type === 'try'">尝试</template>
<template v-else-if="type === 'catch'">捕获</template>
<template v-else-if="type === 'finally'">最后</template>
<template v-else>结束</template>
</div>
<!-- 错误变量输入区域 -->
<div class="try-catch-settings">
<template v-if="type === 'catch'">
<ControlInput
:model-value="argvs.errorVar"
@update:model-value="updateArgvs('errorVar', $event)"
label="错误"
placeholder="错误变量名"
class="error-input"
/>
</template>
</div>
<!-- 只在try开始时显示添加按钮 -->
<q-btn-dropdown
v-if="type === 'try'"
flat
dense
dropdown-icon="add"
no-icon-animation
size="sm"
class="control-btn"
>
<q-list>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'catch',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加捕获分支</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'finally',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加最后分支</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ControlInput from "components/composer/common/ControlInput.vue";
export default defineComponent({
name: "TryCatchControl",
components: {
ControlInput,
},
inheritAttrs: false,
props: {
modelValue: Object,
},
emits: ["update:modelValue", "addBranch"],
data() {
return {
defaultArgvs: {
errorVar: "error",
},
};
},
computed: {
type() {
return this.modelValue.commandType;
},
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
},
methods: {
updateArgvs(key, value) {
const argvs = { ...this.argvs, [key]: value };
this.updateModelValue(argvs);
},
generateCode(argvs = this.argvs) {
switch (this.type) {
case "try":
return "try{";
case "catch":
return `}catch(${argvs.errorVar || "error"}){`;
case "finally":
return "}finally{";
case "end":
return "}";
default:
return "";
}
},
parseCodeToArgvs(code) {
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
if (!code) return argvs;
if (this.type === "catch") {
const match = code.match(/^\}catch\((\w+)\)\{$/);
if (match) {
argvs.errorVar = match[1];
}
}
return argvs;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
argvs,
code: this.generateCode(argvs),
});
},
},
mounted() {
if (!this.modelValue.argvs && !this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
});
</script>
<style scoped>
.try-catch-wrapper {
width: 100%;
display: flex;
}
.try-catch {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.control-type-label {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0.9;
user-select: none;
}
.control-btn {
width: 21px;
height: 21px;
opacity: 0.7;
flex-shrink: 0;
}
.control-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.try-catch-settings {
display: flex;
gap: 4px;
flex: 1;
min-width: 0;
}
.error-input {
flex: 1;
min-width: 0;
}
/* 暗色模式适配 */
.body--dark .control-btn {
color: rgba(255, 255, 255, 0.7);
}
.body--dark .control-btn:hover {
color: var(--q-primary);
}
</style>

View File

@ -1,205 +0,0 @@
<template>
<div class="loop-control-wrapper">
<div class="loop-control">
<!-- 类型标签 -->
<div class="control-type-label">
<template v-if="type === 'while'">满足</template>
<template v-else-if="type === 'continue'">继续</template>
<template v-else-if="type === 'break'">终止</template>
<template v-else>结束</template>
</div>
<!-- 循环条件区域 -->
<div class="loop-settings">
<template v-if="type === 'while'">
<ControlInput
:model-value="argvs.condition"
@update:model-value="updateArgvs('condition', $event)"
label="条件"
placeholder="表达式"
class="condition-input"
/>
</template>
</div>
<!-- 只在循环开始时显示添加按钮 -->
<q-btn-dropdown
v-if="type === 'while'"
flat
dense
dropdown-icon="add"
no-icon-animation
size="sm"
class="control-btn"
>
<q-list>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'continue',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加继续循环</q-item-label>
</q-item-section>
</q-item>
<q-item
clickable
v-close-popup
@click="
$emit('addBranch', {
chainId: this.modelValue.chainId,
commandType: 'break',
value: this.modelValue.value,
})
"
>
<q-item-section>
<q-item-label>添加终止循环</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</div>
</template>
<script>
import { defineComponent } from "vue";
import ControlInput from "components/composer/common/ControlInput.vue";
export default defineComponent({
name: "WhileControl",
components: {
ControlInput,
},
inheritAttrs: false,
props: {
modelValue: Object,
},
emits: ["update:modelValue", "addBranch"],
data() {
return {
defaultArgvs: {
condition: "true",
},
};
},
computed: {
type() {
return this.modelValue.commandType;
},
argvs() {
return (
this.modelValue.argvs || this.parseCodeToArgvs(this.modelValue.code)
);
},
},
methods: {
updateArgvs(key, value) {
const argvs = { ...this.argvs, [key]: value };
this.updateModelValue(argvs);
},
generateCode(argvs = this.argvs) {
switch (this.type) {
case "while":
return `while(${argvs.condition || "true"}){`;
case "continue":
return "continue;";
case "break":
return "break;";
case "end":
return "}";
default:
return "";
}
},
parseCodeToArgvs(code) {
const argvs = window.lodashM.cloneDeep(this.defaultArgvs);
if (!code) return argvs;
switch (this.type) {
case "while":
const whileMatch = code.match(/^while\((.*?)\)\{$/);
if (whileMatch) {
argvs.condition = whileMatch[1] === "true" ? "" : whileMatch[1];
}
break;
}
return argvs;
},
updateModelValue(argvs) {
this.$emit("update:modelValue", {
...this.modelValue,
argvs,
code: this.generateCode(argvs),
});
},
},
mounted() {
if (!this.modelValue.argvs && !this.modelValue.code) {
this.updateModelValue(this.defaultArgvs);
}
},
});
</script>
<style scoped>
.loop-control-wrapper {
width: 100%;
display: flex;
}
.loop-control {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
}
.control-type-label {
font-size: 13px;
font-weight: 500;
white-space: nowrap;
opacity: 0.9;
user-select: none;
}
.control-btn {
width: 21px;
height: 21px;
opacity: 0.7;
flex-shrink: 0;
}
.control-btn:hover {
opacity: 1;
transform: scale(1.1);
}
.loop-settings {
display: flex;
gap: 4px;
flex: 1;
min-width: 0;
}
.condition-input {
flex: 1;
min-width: 0;
}
/* 暗色模式适配 */
.body--dark .control-btn {
color: rgba(255, 255, 255, 0.7);
}
.body--dark .control-btn:hover {
color: var(--q-primary);
}
</style>

View File

@ -11,29 +11,6 @@ export const KeySequenceEditor = defineAsyncComponent(() =>
import("src/components/composer/simulate/KeySequenceEditor.vue")
);
// 控制流组件
export const ConditionalJudgment = defineAsyncComponent(() =>
import("components/composer/control/ConditionalJudgment.vue")
);
export const LoopControl = defineAsyncComponent(() =>
import("components/composer/control/LoopControl.vue")
);
export const ForEachControl = defineAsyncComponent(() =>
import("components/composer/control/ForEachControl.vue")
);
export const ForInControl = defineAsyncComponent(() =>
import("components/composer/control/ForInControl.vue")
);
export const WhileControl = defineAsyncComponent(() =>
import("components/composer/control/WhileControl.vue")
);
export const SwitchControl = defineAsyncComponent(() =>
import("components/composer/control/SwitchControl.vue")
);
export const TryCatchControl = defineAsyncComponent(() =>
import("components/composer/control/TryCatchControl.vue")
);
// 网络组件
export const UBrowserEditor = defineAsyncComponent(() =>
import("components/composer/ubrowser/UBrowserEditor.vue")

View File

@ -17,7 +17,7 @@ export const codingCommands = {
type: "varInput",
},
],
functionSelector: [
subCommands: [
{
label: "Base64编码",
value: "quickcomposer.coding.base64Encode",
@ -88,7 +88,7 @@ export const codingCommands = {
type: "varInput",
},
],
functionSelector: [
subCommands: [
{
label: "MD5",
value: "quickcomposer.coding.md5Hash",

View File

@ -5,51 +5,358 @@ export const controlCommands = {
{
value: "condition",
label: "条件判断",
component: "ConditionalJudgment",
component: "ControlCommand",
isControlFlow: true,
commandChain: ["if", "end"],
subCommands: [
{
label: "如果满足",
value: "if",
codeTemplate: "if (${condition}) {",
config: [
{
key: "condition",
label: "条件",
type: "controlInput",
placeholder: "表达式",
defaultValue: "true",
},
],
},
{
label: "否则",
value: "else",
icon: "fork_left",
codeTemplate: "} else {",
},
{
label: "否则满足",
value: "else if",
icon: "fork_left",
codeTemplate: "} else if (${condition}) {",
config: [
{
key: "condition",
label: "条件",
type: "controlInput",
placeholder: "表达式",
},
],
},
{
label: "结束",
value: "end",
codeTemplate: "}",
},
],
},
{
value: "loop",
label: "循环执行",
component: "LoopControl",
component: "ControlCommand",
isControlFlow: true,
commandChain: ["loop", "end"],
subCommands: [
{
label: "循环执行",
value: "loop",
icon: "loop",
codeTemplate:
"for (let ${indexVar} = ${startValue}; ${indexVar} <= ${endValue}; ${indexVar} += ${stepValue}) {",
config: [
{
key: "indexVar",
label: "变量",
type: "controlInput",
defaultValue: "i",
width: 3,
},
{
key: "startValue",
label: "从",
type: "controlInput",
icon: "first_page",
defaultValue: "0",
width: 3,
},
{
key: "endValue",
label: "到",
type: "controlInput",
icon: "last_page",
defaultValue: "10",
width: 3,
},
{
key: "stepValue",
label: "步进",
type: "controlInput",
icon: "trending_up",
defaultValue: "1",
width: 3,
},
],
},
{
label: "继续循环",
value: "continue",
icon: "skip_next",
codeTemplate: "continue;",
},
{
label: "终止循环",
value: "break",
icon: "stop",
codeTemplate: "break;",
},
{
label: "结束",
value: "end",
codeTemplate: "}",
},
],
},
{
value: "forEach",
label: "遍历数组",
component: "ForEachControl",
component: "ControlCommand",
isControlFlow: true,
commandChain: ["forEach", "end"],
subCommands: [
{
label: "遍历数组",
value: "forEach",
icon: "list",
codeTemplate:
"for (let [${indexVar}, ${itemVar}] of ${arrayVar}.entries()) {",
config: [
{
key: "itemVar",
label: "元素",
type: "controlInput",
defaultValue: "item",
width: 4,
},
{
key: "indexVar",
label: "索引",
type: "controlInput",
defaultValue: "i",
width: 4,
},
{
key: "arrayVar",
label: "数组",
type: "controlInput",
icon: "list",
defaultValue: "array",
width: 4,
},
],
},
{
label: "继续循环",
value: "continue",
icon: "skip_next",
codeTemplate: "continue;",
},
{
label: "终止循环",
value: "break",
icon: "stop",
codeTemplate: "break;",
},
{
label: "结束",
value: "end",
codeTemplate: "}",
},
],
},
{
value: "forIn",
label: "遍历对象",
component: "ForInControl",
component: "ControlCommand",
isControlFlow: true,
commandChain: ["forIn", "end"],
subCommands: [
{
label: "遍历对象",
value: "forIn",
icon: "data_object",
codeTemplate:
"for (const ${keyVar} in ${objectVar}) { const ${valueVar} = ${objectVar}[${keyVar}];",
config: [
{
key: "keyVar",
label: "键名",
type: "controlInput",
defaultValue: "key",
width: 4,
},
{
key: "valueVar",
label: "值",
type: "controlInput",
defaultValue: "value",
width: 4,
},
{
key: "objectVar",
label: "对象",
type: "controlInput",
defaultValue: "object",
width: 4,
},
],
},
{
label: "继续循环",
value: "continue",
icon: "skip_next",
codeTemplate: "continue;",
},
{
label: "终止循环",
value: "break",
icon: "stop",
codeTemplate: "break;",
},
{
label: "结束",
value: "end",
codeTemplate: "}",
},
],
},
{
value: "while",
label: "条件循环",
component: "WhileControl",
component: "ControlCommand",
isControlFlow: true,
commandChain: ["while", "end"],
subCommands: [
{
label: "条件循环",
value: "while",
icon: "loop",
codeTemplate: "while (${condition}) {",
config: [
{
key: "condition",
label: "条件",
type: "controlInput",
placeholder: "表达式",
defaultValue: "true",
},
],
},
{
label: "继续循环",
value: "continue",
icon: "skip_next",
codeTemplate: "continue;",
},
{
label: "终止循环",
value: "break",
icon: "stop",
codeTemplate: "break;",
},
{
label: "结束",
value: "end",
codeTemplate: "}",
},
],
},
{
value: "switch",
label: "条件分支",
component: "SwitchControl",
component: "ControlCommand",
isControlFlow: true,
commandChain: ["switch", "case", "end"],
subCommands: [
{
label: "条件分支",
value: "switch",
icon: "call_split",
codeTemplate: "switch (${expression}) {",
config: [
{
key: "expression",
label: "变量",
type: "controlInput",
placeholder: "变量或表达式",
defaultValue: "expression",
},
],
},
{
label: "匹配分支",
value: "case",
icon: "check",
codeTemplate: "case ${value}:",
config: [
{
key: "value",
label: "值",
type: "controlInput",
},
],
},
{
label: "默认分支",
value: "default",
icon: "last_page",
codeTemplate: "default:",
},
{
label: "结束",
value: "end",
codeTemplate: "}",
},
],
},
{
value: "tryCatch",
label: "异常处理",
component: "TryCatchControl",
component: "ControlCommand",
isControlFlow: true,
commandChain: ["try", "catch", "end"],
subCommands: [
{
label: "尝试执行",
value: "try",
icon: "play_circle",
codeTemplate: "try {",
},
{
label: "捕获异常",
value: "catch",
icon: "error",
codeTemplate: "} catch (${errorVar}) {",
config: [
{
key: "errorVar",
label: "错误",
type: "controlInput",
defaultValue: "error",
},
],
},
{
label: "最后执行",
value: "finally",
icon: "done_all",
codeTemplate: "} finally {",
},
{
label: "结束",
value: "end",
codeTemplate: "}",
},
],
},
],
};

View File

@ -93,7 +93,7 @@ export const dataCommands = {
desc: "Buffer创建、转换和操作",
config: [],
icon: "memory",
functionSelector: [
subCommands: [
{
value: "quickcomposer.data.buffer.from",
label: "创建Buffer",

View File

@ -17,7 +17,7 @@ export const mathCommands = {
type: "numInput",
},
],
functionSelector: [
subCommands: [
{
label: "正弦(sin)",
value: "Math.sin",

View File

@ -61,7 +61,7 @@ export const networkCommands = {
width: "auto",
},
],
functionSelector: [
subCommands: [
{
value: "quickcomposer.network.url.parse",
label: "解析URL",
@ -239,7 +239,7 @@ export const networkCommands = {
width: "auto",
},
],
functionSelector: [
subCommands: [
{
label: "DNS查询",
value: "quickcomposer.network.dns.lookupHost",

View File

@ -19,7 +19,7 @@ export const simulateCommands = {
value: "quickcommand.simulateCopy",
label: "模拟复制粘贴",
config: [],
functionSelector: [
subCommands: [
{
value: "quickcommand.simulateCopy",
label: "复制",
@ -73,7 +73,7 @@ export const simulateCommands = {
width: 6,
},
],
functionSelector: [
subCommands: [
{
label: "单击",
value: "utools.simulateMouseClick",
@ -94,7 +94,7 @@ export const simulateCommands = {
{
value: "utools.simulateMouseMove",
label: "鼠标位置",
functionSelector: [
subCommands: [
{
label: "移动到坐标",
value: "utools.simulateMouseMove",

View File

@ -22,7 +22,7 @@ export const systemCommands = {
label: "获取剪贴板内容",
outputVariable: "clipboardContent",
saveOutput: true,
functionSelector: [
subCommands: [
{
value: "electron.clipboard.readText",
label: "剪贴板文本",
@ -59,7 +59,7 @@ export const systemCommands = {
desc: "获取操作系统相关信息",
icon: "computer",
config: [],
functionSelector: [
subCommands: [
{
value: "quickcomposer.system.os.arch",
label: "系统架构",
@ -162,7 +162,7 @@ export const systemCommands = {
width: "auto",
},
],
functionSelector: [
subCommands: [
{
value: "quickcomposer.system.path.normalize",
label: "规范化路径",

View File

@ -56,7 +56,7 @@ const commonComponentGuide = {
]
`,
},
functionSelector: {
subCommands: {
description:
"可选,函数选择器配置,用于一个命令包含多个相关函数的情况",
properties: {
@ -67,7 +67,7 @@ const commonComponentGuide = {
excludeConfig: "可选,要排除的通用参数索引数组",
},
example: `
functionSelector: [
subCommands: [
{
label: "DNS查询",
value: "quickcomposer.network.dns.lookupHost",

View File

@ -1,369 +0,0 @@
{
"项目结构说明": {
"src": {
"描述": "源代码目录",
"js": {
"描述": "存放JS代码",
"composer": {
"描述": "存放可视化编排相关的JS代码",
"commands": {
"描述": "存放生成组件的配置文件,一个分类一个文件",
"命令配置字段说明": {
"value": "必选当使用MultiParams组件且未启用functionSelector时表示生成代码时使用的函数名",
"label": "必选,命令的显示的名称",
"component": "可选存在则表示使用自定义组件否则根据config使用MultiParams组件",
"isControlFlow": "可选,是否是流程控制命令",
"commandChain": "可选,命令链,流程控制命令使用",
"allowEmptyArgv": "可选,是否允许空参数",
"isAsync": "可选,是否是异步命令",
"config": {
"描述": "可选命令的配置用来在MultiParams组件中显示是一个数组每个元素是一个对象",
"配置项属性": {
"key": "必选,键",
"label": "必选,标签",
"type": "必选类型varInput则使用VariableInput组件,numInput则使用NumberInput组件,其他类型则使用qurasr对应组件",
"defaultValue": "可选,默认值",
"icon": "必选,图标",
"width": "可选flex布局的相对比例"
}
},
"functionSelector": {
"配置项属性": {
"label": "必选,显示的名称",
"value": "必选,生成代码时使用的函数名",
"icon": "可选,图标",
"config": "可选特定函数独有参数配置和MultiParams组件的config一样"
},
"value": "必选,默认值"
}
}
},
"cardComponents.js": {
"描述": "自定义的组件要在这里导入",
"主要功能": {
"导入组件": "导入并注册自定义的命令卡片组件",
"导出组件": "导出所有注册的组件供CommandComposer使用"
}
},
"customComponentGuide.js": {
"描述": "自定义组件创建指南"
},
"formatString.js": {
"描述": "处理字符串格式化的工具函数,主要用于处理对象序列化和类型转换",
"主要功能": {
"parseFunction": "解析函数字符串,返回函数名和参数",
"stringifyArgv": "格式化参数为字符串,根据值的类型决定是否添加引号"
}
}
}
},
"components": {
"描述": "存放Vue组件",
"composer": {
"描述": "存放可视化编排相关的组件",
"file": {
"描述": "存放文件操作相关的组件",
"组件列表": {
"FileOperationEditor.vue": {
"描述": "文件操作编辑器组件",
"主要功能": {
"文件读取": {
"编码支持": "UTF-8、ASCII、Base64、二进制、十六进制",
"读取模式": "全部读取、指定位置读取",
"参数配置": "起始位置、读取长度"
},
"文件写入": {
"编码支持": "UTF-8、ASCII、Base64、二进制、十六进制",
"写入模式": "覆盖写入、追加写入",
"内容输入": "支持变量和文本输入"
},
"文件删除": {
"删除选项": "递归删除、强制删除"
},
"文件管理": {
"重命名/移动": "支持文件重命名和移动",
"权限管理": "支持修改文件权限,支持递归修改",
"所有者管理": "支持修改文件所有者和组,支持递归修改"
}
}
}
}
},
"ui": {
"描述": "基础UI组件",
"组件列表": {
"VariableInput.vue": {
"描述": "统一处理变量输入和输出组件",
"主要功能": {
"变量输入": "支持各种类型的变量输入",
"引号处理": "自动处理返回值的引号",
"类型转换": "支持不同类型间的转换"
}
},
"MultiParams.vue": {
"描述": "统一处理多参数输入和输出组件",
"主要功能": {
"参数配置": "根据config配置显示不同的参数输入",
"代码生成": "生成完整的命令代码",
"参数验证": "验证参数的完整性和正确性"
}
},
"DictionaryEditor.vue": {
"描述": "统一处理字典输入和输出组件",
"主要功能": {
"键值对编辑": "支持编辑字典的键值对",
"对象转换": "自动处理对象的序列化和反序列化"
}
}
}
},
"control": {
"描述": "存放控制流程的组件",
"ConditionalJudgment.vue": "条件判断组件用于if/else等流程控制",
"LoopControl.vue": "循环控制组件用于for/while等循环控制",
"ForEachControl.vue": "foreach循环控制组件用于foreach循环控制",
"ForInControl.vue": "forin循环控制组件用于forin循环控制",
"WhileControl.vue": "while循环控制组件用于while循环控制",
"SwitchControl.vue": "switch控制组件用于switch控制"
},
"simulate": {
"描述": "存放模拟操作的组件",
"ImageSearchEditor.vue": "图片搜索组件,用于搜索图片"
},
"crypto": {
"描述": "存放加解密的组件",
"SymmetricCryptoEditor.vue": "对称加解密组件,用于对称加解密",
"AsymmetricCryptoEditor.vue": "非对称加解密组件,用于非对称加解密"
},
"card": {
"描述": "存放ComposerCard子组件",
"CommandHead.vue": "命令卡片的头部组件,包含命令名称、折叠、删除等功能",
"CommandButtons.vue": "命令卡片的按钮组件,包含运行、保存等功能"
},
"flow": {
"描述": "存放ComposerFlow子组件",
"ComposerButtons.vue": "流程操作按钮组件,包含运行、保存等功能",
"ChainStyles.vue": "控制流程链样式组件",
"DropArea.vue": "在命令流程下面显示一个拖拽区域,用于拖拽命令到流程编辑器",
"EmptyFlow.vue": "没有流程时显示的组件"
},
"http": {
"描述": "存放http请求的组件",
"AxiosConfigEditor.vue": "Axios请求配置编辑器"
},
"regex": {
"描述": "存放正则相关的组件"
},
"CommandComposer.vue": {
"描述": "命令编辑器主组件",
"主要功能": {
"变量管理": "管理命令间的变量传递",
"命令流程": "管理命令的执行顺序",
"代码生成": "生成最终的执行代码"
},
"子组件": {
"ComposerList": "左侧命令列表",
"ComposerFlow": "右侧流程编辑器"
}
},
"ComposerCard.vue": {
"描述": "命令卡片组件,用于显示和编辑单个命令",
"主要功能": {
"参数编辑": "根据命令配置显示不同的参数编辑器",
"变量输出": "管理命令的输出变量",
"折叠控制": "控制命令卡片的折叠状态"
}
},
"ComposerFlow.vue": {
"描述": "命令流程编辑器,管理命令的执行顺序",
"主要功能": {
"拖拽排序": "通过拖拽调整命令顺序",
"链式命令": "管理if/else等控制流程命令",
"折叠管理": "控制命令链的折叠状态"
}
},
"ComposerList.vue": {
"描述": "命令列表组件,显示可用的命令",
"主要功能": {
"命令分类": "按类别显示命令",
"搜索过滤": "支持拼音搜索命令",
"拖拽添加": "拖拽命令到流程编辑器"
}
}
},
"quickcommandUI": {
"描述": "存放quickcommand的UI组件提供各种交互界面",
"组件列表": {
"QuickCommand.vue": {
"描述": "UI组件的主入口",
"主要功能": {
"对话框管理": {
"showInputBox": "显示输入框对话框,支持多个输入框",
"showButtonBox": "显示按钮对话框,支持多个按钮",
"showConfirmBox": "显示确认对话框支持HTML内容",
"showMessageBox": "显示消息提示框,支持不同图标和位置",
"showTextArea": "显示文本编辑框,支持大段文本输入",
"showSelectList": "显示选择列表,支持搜索和快捷键"
},
"按钮管理": {
"showWaitButton": "显示等待按钮",
"closeWaitButton": "关闭等待按钮"
},
"事件管理": {
"listenKeydown": "添加键盘事件监听",
"removeListener": "移除键盘事件监听"
}
}
},
"子组件": {
"InputBox.vue": {
"描述": "输入框对话框组件",
"功能": {
"多输入框": "支持显示多个输入框",
"提示文本": "支持设置标签和提示文本",
"默认值": "支持设置默认值"
}
},
"ButtonBox.vue": {
"描述": "按钮对话框组件",
"功能": {
"多按钮": "支持显示多个按钮",
"返回值": "返回点击的按钮索引和文本"
}
},
"ConfirmBox.vue": {
"描述": "确认对话框组件",
"功能": {
"HTML内容": "支持显示HTML格式的内容",
"自定义宽度": "支持设置对话框宽度"
}
},
"TextArea.vue": {
"描述": "文本编辑框组件",
"功能": {
"大文本输入": "支持输入和编辑大段文本",
"占位文本": "支持设置占位文本",
"默认值": "支持设置默认值"
}
},
"SelectList.vue": {
"描述": "选择列表组件",
"功能": {
"虚拟滚动": "使用虚拟滚动优化大量数据的显示",
"拼音搜索": "支持拼音搜索过滤",
"键盘导航": "支持键盘上下键导航",
"快捷键选择": "支持Alt/Command+数字快速选择",
"多种显示模式": {
"plaintext": "纯文本模式",
"json": "带图标和描述的JSON模式",
"html": "HTML内容模式"
}
}
}
}
}
}
}
},
"plugin": {
"描述": "插件相关代码",
"lib": {
"描述": "存放核心功能代码",
"文件列表": {
"quickcomposer": {
"描述": "命令生成器的核心功能",
"命名规则": "quickcomposer.xxx.yyy.js 对应 quickcomposer.xxx.yyy 接口xxx 为分类和src/js/composer/commands里分类对应",
"功能分类": {
"系统命令": "系统相关的命令",
"控制命令": "流程控制相关的命令",
"模拟命令": "模拟操作相关的命令"
}
},
"quickcommand.js": {
"描述": "定义preload.js中quickcommand的接口UI相关的接口在src/components/quickcommandUI中",
"主要功能": {
"模拟操作": {
"simulateCopy": "模拟复制操作",
"simulatePaste": "模拟粘贴操作"
},
"剪贴板": {
"readClipboard": "读取剪贴板文本",
"writeClipboard": "写入剪贴板文本",
"readClipboardImage": "读取剪贴板图片"
},
"文件操作": {
"downloadFile": "下载文件",
"uploadFile": "上传文件",
"loadRemoteScript": "载入在线资源"
},
"系统操作": {
"sleep": "同步延时",
"setTimeout": "异步延时",
"kill": "关闭进程",
"wakeUtools": "唤醒uTools",
"runInTerminal": "在终端中执行命令(非Linux)"
},
"平台特定": {
"Windows": {
"runVbs": "运行VBS脚本",
"runPowerShell": "运行PowerShell脚本",
"runCsharp": "运行C#脚本"
},
"MacOS": {
"runAppleScript": "运行AppleScript脚本"
}
}
}
},
"getQuickcommandTempFile.js": {
"描述": "获取临时文件路径",
"参数": {
"ext": "文件扩展名",
"name": "可选,文件名,默认为时间戳",
"dir": "可选临时目录名默认为quickcommandTempDir"
}
},
"lodashMini.js": {
"描述": "lodash的精简版提供常用的工具函数",
"主要函数": {
"数组操作": {
"concat": "连接数组",
"pull": "移除数组中的指定值",
"union": "创建去重数组",
"without": "创建不包含指定值的数组"
},
"对象操作": {
"cloneDeep": "深拷贝",
"pick": "选取对象属性",
"omitBy": "过滤对象属性",
"values": "获取对象的所有值"
},
"集合操作": {
"filter": "过滤集合",
"forIn": "遍历对象的可枚举属性"
},
"类型判断": {
"isArray": "是否为数组",
"isBuffer": "是否为Buffer",
"isEmpty": "是否为空",
"isNil": "是否为null或undefined",
"isObject": "是否为对象"
},
"字符串操作": {
"truncate": "截断字符串"
}
}
}
}
},
"preload.js": {
"描述": "预加载脚本",
"主要功能": {
"接口暴露": "暴露接口给渲染进程",
"沙箱环境": "提供安全的代码执行环境",
"全局变量": "管理全局变量和工具函数"
}
}
}
}
}