重构Composer组件中的变量管理:改为FlowManager,支持函数名和标识的修改,新增函数时自动弹出管理界面

This commit is contained in:
fofolee 2025-01-22 18:28:07 +08:00
parent 957c6021b8
commit b33ef1be7f
7 changed files with 131 additions and 86 deletions

View File

@ -148,18 +148,18 @@ export default defineComponent({
},
},
setup(props) {
const getCurrentVariables = inject("getCurrentVariables");
const getCurrentExistingVar = inject("getCurrentExistingVar");
// commandIndex
const commandIndex = computed(() => props.commandIndex);
// VariableInput
provide("commandIndex", commandIndex);
return { getCurrentVariables };
return { getCurrentExistingVar };
},
methods: {
handleOutputVariableUpdate(value) {
const result = processVariable({
value,
existingVars: this.getCurrentVariables().map((v) => v.name),
existingVars: this.getCurrentExistingVar().map((v) => v.name),
});
if (result.warning) {

View File

@ -92,8 +92,8 @@ export default defineComponent({
},
},
setup() {
const getCurrentVariables = inject("getCurrentVariables");
return { getCurrentVariables };
const getCurrentExistingVar = inject("getCurrentExistingVar");
return { getCurrentExistingVar };
},
emits: ["update:modelValue", "add-command", "action"],
data() {
@ -296,7 +296,7 @@ export default defineComponent({
if (newCommand.saveOutput && newCommand.outputVariable) {
newCommand.outputVariable = processVariable({
value: newCommand.outputVariable,
existingVars: this.getCurrentVariables().map((v) => v.name),
existingVars: this.getCurrentExistingVar().map((v) => v.name),
}).processedValue;
}
return newCommand;

View File

@ -8,9 +8,12 @@
dense
label="主流程"
class="main-btn"
@dblclick="editFunction(mainFlow)"
:class="{ 'main-btn-active': activeTab === 'main' }"
@click="activeTab = 'main'"
/>
>
<q-tooltip>双击管理</q-tooltip>
</q-btn>
<!-- 其他流程标签可滚动 -->
<q-tabs
@ -34,23 +37,9 @@
<template #item="{ element: flow }">
<q-tab :name="flow.id" class="flow-tab" no-caps>
<div class="flow-tab-content">
<template v-if="flow.isEditing">
<q-input
v-model="flow.label"
dense
borderless
class="flow-name-input"
@keydown.space.prevent
@blur="finishEdit(flow)"
@keyup.enter="finishEdit(flow)"
ref="inputRefs"
/>
</template>
<template v-else>
<span class="flow-name-text" @dblclick="startEdit(flow)">{{
flow.label
}}</span>
</template>
<span class="flow-name-text" @dblclick="editFunction(flow)">
{{ flow.label }}
</span>
<q-btn
flat
dense
@ -60,7 +49,7 @@
@click.stop="removeFlow(flow)"
/>
</div>
<q-tooltip> 双击修改名称拖动排序 </q-tooltip>
<q-tooltip> 双击管理 </q-tooltip>
</q-tab>
</template>
</draggable>
@ -91,10 +80,11 @@
@action="(type, payload) => handleAction(type, payload)"
ref="flowRefs"
/>
<VariableManager
<FlowManager
v-model="showVariableManager"
:flow="flow"
:variables="flow.customVariables"
@update:variables="flow.customVariables = $event"
@update-flow="updateFlow(flow)"
:is-main-flow="flow.id === 'main'"
:output-variables="outputVariables"
class="variable-panel"
@ -105,13 +95,13 @@
<script>
import { defineComponent, provide, ref, computed } from "vue";
import ComposerFlow from "./ComposerFlow.vue";
import ComposerButtons from "./flow/ComposerButtons.vue";
import VariableManager from "./flow/VariableManager.vue";
import draggable from "vuedraggable";
import ComposerFlow from "components/composer/ComposerFlow.vue";
import ComposerButtons from "components/composer/flow/ComposerButtons.vue";
import FlowManager from "components/composer/flow/FlowManager.vue";
import { generateCode } from "js/composer/generateCode";
import { findCommandByValue } from "js/composer/composerConfig";
import { generateUniqSuffix } from "js/composer/variableManager";
import draggable from "vuedraggable";
import { parseVariables } from "js/composer/variableManager";
export default defineComponent({
name: "FlowTabs",
@ -119,7 +109,7 @@ export default defineComponent({
ComposerFlow,
ComposerButtons,
draggable,
VariableManager,
FlowManager,
},
props: {
showCloseButton: {
@ -143,7 +133,7 @@ export default defineComponent({
return subFlows.value.map((flow) => {
return {
label: flow.label,
value: flow.name,
name: flow.name,
id: flow.id,
};
});
@ -209,6 +199,12 @@ export default defineComponent({
provide("getCurrentVariables", getCurrentVariables);
const getCurrentExistingVar = () => {
return [...getCurrentVariables(), ...getCurrentFunctions()];
};
provide("getCurrentExistingVar", getCurrentExistingVar);
return {
flows,
mainFlow,
@ -246,6 +242,9 @@ export default defineComponent({
customVariables: [],
});
this.activeTab = id;
this.$nextTick(() => {
this.toggleVariableManager();
});
},
removeFlow(flow) {
const index = this.subFlows.findIndex((f) => f.id === flow.id);
@ -281,13 +280,16 @@ export default defineComponent({
this.expandAll();
break;
case "toggleVariableManager":
this.showVariableManager = !this.showVariableManager;
this.outputVariables = this.getOutputVariables();
this.toggleVariableManager();
break;
default:
this.$emit("action", type, this.generateAllFlowCode());
}
},
toggleVariableManager() {
this.showVariableManager = !this.showVariableManager;
this.outputVariables = this.getOutputVariables();
},
saveFlows() {
const flowsData = this.flows.map((flow) => ({
...flow,
@ -328,8 +330,7 @@ export default defineComponent({
};
}),
}));
this.mainFlow = newFlows[0];
this.subFlows = newFlows.slice(1);
this.updateFlow(newFlows);
this.activeTab = this.mainFlow.id;
},
runFlows(flow) {
@ -353,20 +354,13 @@ export default defineComponent({
});
this.isAllCollapsed = false;
},
startEdit(flow) {
flow.isEditing = true;
this.$nextTick(() => {
const input = this.$refs.inputRefs?.[0];
if (input) {
input.focus();
}
});
editFunction(flow) {
this.activeTab = flow.id;
this.toggleVariableManager();
},
finishEdit(flow) {
flow.isEditing = false;
if (!flow.label) {
flow.label = this.generateFlowName().replace("func_", "函数");
}
updateFlow(flow) {
this.mainFlow = flow[0];
this.subFlows = flow.slice(1);
},
},
});

View File

@ -48,12 +48,12 @@
:key="func.id"
clickable
v-close-popup
@click="insertValue(func.value + getInsertFunctionParams(func.id))"
@click="insertValue(func.name + getInsertFunctionParams(func.id))"
class="variable-item"
>
<q-item-section>
<q-item-label class="variable-name">
{{ func.value }}
{{ func.name }}
</q-item-label>
<q-item-label caption class="variable-source">
{{ func.label }}
@ -97,7 +97,7 @@
<div class="item">
<span>标签栏右侧</span>
<q-badge color="primary">
<q-icon name="data_object" size="10px" />
<q-icon name="settings" size="10px" />
</q-badge>
<span>按钮</span>
</div>

View File

@ -12,12 +12,12 @@
</q-btn>
<q-separator vertical />
<q-btn
icon="data_object"
icon="settings"
dense
flat
@click="$emit('action', 'toggleVariableManager')"
>
<q-tooltip>变量管理</q-tooltip>
<q-tooltip>流程管理</q-tooltip>
</q-btn>
<q-separator vertical />
<q-btn

View File

@ -2,7 +2,7 @@
<div class="variable-manager" :class="{ 'is-visible': modelValue }">
<div class="variable-content">
<div class="variable-header">
<div class="header-title">变量管理</div>
<div class="header-title">流程管理</div>
<q-btn
flat
dense
@ -12,6 +12,40 @@
@click="$emit('update:modelValue', false)"
/>
</div>
<!-- 函数信息编辑 (只在非主流程显示) -->
<template v-if="!isMainFlow">
<div class="section">
<div class="var-list">
<div class="var-item">
<q-input
v-model="localFlow.label"
dense
borderless
class="var-input"
>
<template #prepend>
<div class="var-label">名称</div>
</template>
</q-input>
</div>
<div class="var-item">
<q-input
v-model="localFlow.name"
dense
borderless
class="var-input"
>
<template #prepend>
<div class="var-label">标识</div>
</template>
</q-input>
</div>
</div>
</div>
<q-separator spaced />
</template>
<!-- 参数管理部分 -->
<div class="section" v-if="!isMainFlow">
<div class="section-header">
@ -112,12 +146,12 @@
<div class="section-header">
<div class="section-title">
<q-icon name="output" size="16px" />
<div>来自输出</div>
<div>输出变量</div>
</div>
</div>
<div class="var-list">
<div
v-for="(variable, index) in outputVariables"
v-for="(variable, index) in localFlow.outputVariables"
:key="index"
class="var-item output-var"
>
@ -134,7 +168,7 @@
import { defineComponent } from "vue";
export default defineComponent({
name: "VariableManager",
name: "FlowManager",
props: {
modelValue: {
type: Boolean,
@ -150,35 +184,36 @@ export default defineComponent({
required: true,
default: () => [],
},
outputVariables: {
type: Array,
flow: {
type: Object,
required: true,
default: () => [],
},
},
emits: ["update:modelValue", "update:variables"],
emits: ["update:modelValue", "update:flow"],
computed: {
customVariables: {
localFlow: {
get() {
return this.variables || [];
return this.flow;
},
set(value) {
this.$emit("update:variables", value);
this.$emit("update:flow", value);
},
},
paramVariables() {
return this.customVariables.filter((v) => v.type === "param");
return this.localFlow.customVariables.filter((v) => v.type === "param");
},
customVars() {
return this.customVariables.filter((v) => v.type === "var");
return this.localFlow.customVariables.filter((v) => v.type === "var");
},
},
methods: {
addVariable(type) {
const prefix = type === "param" ? "param_" : "var_";
const count = this.customVariables.filter((v) => v.type === type).length;
this.customVariables = [
...this.customVariables,
const count = this.localFlow.customVariables.filter(
(v) => v.type === type
).length;
this.localFlow.customVariables = [
...this.localFlow.customVariables,
{
name: prefix + (count + 1),
type,
@ -187,18 +222,22 @@ export default defineComponent({
];
},
removeVariable(index, type) {
const typeVars = this.customVariables.filter((v) => v.type === type);
const globalIndex = this.customVariables.indexOf(typeVars[index]);
const typeVars = this.localFlow.customVariables.filter(
(v) => v.type === type
);
const globalIndex = this.localFlow.customVariables.indexOf(
typeVars[index]
);
if (globalIndex > -1) {
const newVars = [...this.customVariables];
const newVars = [...this.localFlow.customVariables];
newVars.splice(globalIndex, 1);
this.customVariables = newVars;
this.localFlow.customVariables = newVars;
}
},
validateVariable(variable, type) {
if (!variable.name) {
const prefix = type === "param" ? "param_" : "var_";
const count = this.customVariables.filter(
const count = this.localFlow.customVariables.filter(
(v) => v.type === type
).length;
variable.name = prefix + count;
@ -227,7 +266,7 @@ export default defineComponent({
}
.variable-manager.is-visible {
width: 200px;
width: 300px;
}
.body--dark .variable-manager {
@ -254,7 +293,7 @@ export default defineComponent({
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px;
padding: 4px 16px;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
margin: -8px -8px 8px -8px;
}
@ -262,7 +301,7 @@ export default defineComponent({
.header-title {
display: flex;
align-items: center;
font-size: 14px;
font-size: 13px;
font-weight: 500;
color: var(--q-primary);
}
@ -281,7 +320,7 @@ export default defineComponent({
}
.section-title {
font-size: 14px;
font-size: 13px;
font-weight: 500;
display: flex;
align-items: center;
@ -327,7 +366,8 @@ export default defineComponent({
flex: 1 1 50%;
}
.var-input :deep(.q-field__control) {
.var-input :deep(.q-field__control),
.var-input :deep(.q-field__prepend) {
height: 20px;
min-height: 20px;
}
@ -360,4 +400,15 @@ export default defineComponent({
font-size: 11px;
opacity: 0.7;
}
.section-content {
padding: 8px;
}
.var-label {
font-size: 12px;
font-weight: 500;
align-items: center;
display: flex;
}
</style>

View File

@ -50,7 +50,7 @@ export function parseVariables(value) {
* @param {string} [currentName] - 当前的变量名如果有
* @returns {string} 有效的变量名
*/
function generateValidVarName(currentName, existingVars) {
function generateValidVarName(currentName, existingVars, baseName = "var") {
// 如果当前名称有效且不重复,直接返回
if (
currentName &&
@ -62,7 +62,7 @@ function generateValidVarName(currentName, existingVars) {
// 如果变量名无效改为var开头
if (!validateVariableName(currentName).isValid) {
currentName = "var";
currentName = baseName;
}
// 添加随机后缀直到不重复
@ -86,7 +86,7 @@ export function generateUniqSuffix(baseName, existingVars, withPrefix = true) {
* @param {string[]} params.existingVars - 当前已存在的变量列表
* @returns {Object} - 处理结果
*/
export function processVariable({ value, existingVars }) {
export function processVariable({ value, existingVars, baseName = "var" }) {
if (!value) {
return { isValid: true, processedValue: value };
}
@ -95,12 +95,12 @@ export function processVariable({ value, existingVars }) {
if (!destructured) {
// 处理单个变量
const processedVar = generateValidVarName(value, existingVars);
const processedVar = generateValidVarName(value, existingVars, baseName);
return {
isValid: true,
processedValue: processedVar,
warning:
processedVar !== value ? `变量名已被修改为: ${processedVar}` : null,
processedVar !== value ? `输入值非法,已被修改为: ${processedVar}` : null,
};
}