重构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) { setup(props) {
const getCurrentVariables = inject("getCurrentVariables"); const getCurrentExistingVar = inject("getCurrentExistingVar");
// commandIndex // commandIndex
const commandIndex = computed(() => props.commandIndex); const commandIndex = computed(() => props.commandIndex);
// VariableInput // VariableInput
provide("commandIndex", commandIndex); provide("commandIndex", commandIndex);
return { getCurrentVariables }; return { getCurrentExistingVar };
}, },
methods: { methods: {
handleOutputVariableUpdate(value) { handleOutputVariableUpdate(value) {
const result = processVariable({ const result = processVariable({
value, value,
existingVars: this.getCurrentVariables().map((v) => v.name), existingVars: this.getCurrentExistingVar().map((v) => v.name),
}); });
if (result.warning) { if (result.warning) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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