加强变量校验,重复和非法时自动重命名,优化变量管理,实时获取最新变量,不再进行增删操作

This commit is contained in:
fofolee 2025-01-07 00:41:28 +08:00
parent 25be7f4926
commit 5947e537fe
10 changed files with 269 additions and 78 deletions

View File

@ -29,6 +29,7 @@ import {
findCommandByValue, findCommandByValue,
} from "js/composer/composerConfig"; } from "js/composer/composerConfig";
import { generateCode } from "js/composer/generateCode"; import { generateCode } from "js/composer/generateCode";
import { parseVariables } from "js/composer/variableManager";
export default defineComponent({ export default defineComponent({
name: "CommandComposer", name: "CommandComposer",
@ -37,37 +38,32 @@ export default defineComponent({
ComposerFlow, ComposerFlow,
}, },
setup() { setup() {
const variables = ref([]); const commandFlow = ref([]);
const addVariable = (name, command) => { //
if (!variables.value.find((v) => v.name === name)) { const getCurrentVariables = () => {
variables.value.push({ const variables = [];
name, for (const cmd of commandFlow.value) {
sourceCommand: command, if (cmd.saveOutput && cmd.outputVariable) {
}); variables.push(
...parseVariables(cmd.outputVariable).map((variable) => ({
name: variable,
sourceCommand: cmd,
}))
);
}
} }
return variables;
}; };
const removeVariable = (name) => { provide("getCurrentVariables", getCurrentVariables);
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 { return {
variables, commandFlow,
addVariable,
removeVariable,
}; };
}, },
data() { data() {
return { return {
commandFlow: [],
availableCommands, availableCommands,
}; };
}, },

View File

@ -66,11 +66,11 @@
<script> <script>
import { defineComponent, inject } from "vue"; import { defineComponent, inject } from "vue";
import { validateVariableName } from "js/common/variableValidator";
import VariableInput from "components/composer/common/VariableInput.vue"; import VariableInput from "components/composer/common/VariableInput.vue";
import MultiParams from "components/composer/MultiParams.vue"; import MultiParams from "components/composer/MultiParams.vue";
import CommandHead from "components/composer/card/CommandHead.vue"; import CommandHead from "components/composer/card/CommandHead.vue";
import * as CardComponents from "js/composer/cardComponents"; import * as CardComponents from "js/composer/cardComponents";
import { processVariable } from "js/composer/variableManager";
export default defineComponent({ export default defineComponent({
name: "ComposerCard", name: "ComposerCard",
@ -110,46 +110,27 @@ export default defineComponent({
}, },
}, },
setup() { setup() {
const addVariable = inject("addVariable"); const getCurrentVariables = inject("getCurrentVariables");
const removeVariable = inject("removeVariable"); return { getCurrentVariables };
const variables = inject("composerVariables", []);
return {
addVariable,
removeVariable,
variables,
};
}, },
methods: { methods: {
handleOutputVariableUpdate(value) { handleOutputVariableUpdate(value) {
// const result = processVariable({
const validation = validateVariableName(value); value,
if (!validation.isValid) { existingVars: this.getCurrentVariables().map((v) => v.name),
quickcommand.showMessageBox(validation.error, "warning"); });
return;
if (result.warning) {
quickcommand.showMessageBox(result.warning, "info");
} }
// this.localCommand.outputVariable = result.processedValue;
if (this.variables.some((v) => v.name === value)) {
quickcommand.showMessageBox(`变量名 "${value}" 已经存在`, "warning");
return;
}
//
if (this.localCommand.outputVariable) {
this.removeVariable(this.localCommand.outputVariable);
}
if (value) {
this.addVariable(value, this.localCommand);
}
this.localCommand.outputVariable = value;
}, },
handleToggleOutput() { handleToggleOutput() {
this.localCommand.saveOutput = !this.localCommand.saveOutput; this.localCommand.saveOutput = !this.localCommand.saveOutput;
// //
if (!this.localCommand.saveOutput && this.localCommand.outputVariable) { if (!this.localCommand.saveOutput) {
this.removeVariable(this.localCommand.outputVariable);
this.localCommand.outputVariable = null; this.localCommand.outputVariable = null;
} }
}, },

View File

@ -74,6 +74,7 @@ import ChainStyles from "./flow/ChainStyles.vue";
import EmptyFlow from "./flow/EmptyFlow.vue"; import EmptyFlow from "./flow/EmptyFlow.vue";
import DropArea from "./flow/DropArea.vue"; import DropArea from "./flow/DropArea.vue";
import { findCommandByValue } from "js/composer/composerConfig"; import { findCommandByValue } from "js/composer/composerConfig";
import { processVariable } from "js/composer/variableManager";
export default defineComponent({ export default defineComponent({
name: "ComposerFlow", name: "ComposerFlow",
@ -96,11 +97,11 @@ export default defineComponent({
required: true, required: true,
}, },
}, },
emits: ["update:modelValue", "add-command", "action"],
setup() { setup() {
const removeVariable = inject("removeVariable"); const getCurrentVariables = inject("getCurrentVariables");
return { removeVariable }; return { getCurrentVariables };
}, },
emits: ["update:modelValue", "add-command", "action"],
data() { data() {
return { return {
dragIndex: -1, dragIndex: -1,
@ -261,13 +262,17 @@ export default defineComponent({
} catch (error) {} } catch (error) {}
}, },
createNewCommand(parsedAction) { createNewCommand(parsedAction) {
return { const newCommand = {
...parsedAction, ...parsedAction,
id: this.getUniqueId(), id: this.getUniqueId(),
saveOutput: false,
useOutput: null,
outputVariable: null,
}; };
if (newCommand.saveOutput && newCommand.outputVariable) {
newCommand.outputVariable = processVariable({
value: newCommand.outputVariable,
existingVars: this.getCurrentVariables().map((v) => v.name),
}).processedValue;
}
return newCommand;
}, },
getUniqueId() { getUniqueId() {
return this.$root.getUniqueId(); return this.$root.getUniqueId();
@ -284,9 +289,6 @@ export default defineComponent({
const cmd = newCommands[i]; const cmd = newCommands[i];
// chainIdchainId // chainIdchainId
if (chainId && cmd.chainId !== chainId) continue; if (chainId && cmd.chainId !== chainId) continue;
if (cmd.outputVariable) {
this.removeVariable(cmd.outputVariable);
}
newCommands.splice(i, 1); newCommands.splice(i, 1);
} }
this.$emit("update:modelValue", newCommands); this.$emit("update:modelValue", newCommands);
@ -326,7 +328,7 @@ export default defineComponent({
command, command,
{ {
// //
code: `${command.outputVariable} && console.log(${command.outputVariable})`, code: `if(${command.outputVariable}!==undefined){console.log(${command.outputVariable})}`,
}, },
]; ];
// //

View File

@ -179,15 +179,13 @@ export default defineComponent({
)?.label; )?.label;
const subFeature = funcNameLabel ? `${funcNameLabel} ` : ""; const subFeature = funcNameLabel ? `${funcNameLabel} ` : "";
const allArgvs = argvs const allArgvs = argvs
.filter((item) => item != null && item != "")
.map((item) => .map((item) =>
item?.hasOwnProperty("__varInputVal__") window.lodashM.truncate(stringifyArgv(item).toString(), {
? window.lodashM.truncate(item.value, { length: 30,
length: 30, omission: "...",
omission: "...", })
}) );
: item
)
.filter((item) => item != null && item != "");
return `${subFeature}${allArgvs.join(",")}`; return `${subFeature}${allArgvs.join(",")}`;
}, },
updateModelValue(funcName, argvs) { updateModelValue(funcName, argvs) {

View File

@ -9,8 +9,9 @@
<!-- 变量输入框 --> <!-- 变量输入框 -->
<q-input <q-input
v-if="command.saveOutput" v-if="command.saveOutput"
:model-value="command.outputVariable" v-model="inputValue"
@update:model-value="$emit('update:outputVariable', $event)" @focus="sourceValue = inputValue"
@blur="handleBlur"
outlined outlined
placeholder="变量名" placeholder="变量名"
class="variable-input" class="variable-input"
@ -91,6 +92,17 @@ export default {
default: false, default: false,
}, },
}, },
data() {
return {
inputValue: this.command.outputVariable || "",
sourceValue: "",
};
},
watch: {
"command.outputVariable"(newVal) {
this.inputValue = newVal || "";
},
},
emits: [ emits: [
"update:outputVariable", "update:outputVariable",
"toggle-output", "toggle-output",
@ -98,6 +110,17 @@ export default {
"remove", "remove",
"toggle-collapse", "toggle-collapse",
], ],
methods: {
handleBlur() {
//
if (
this.inputValue.replace(/[ ]/g, "") ===
this.sourceValue.replace(/[ ]/g, "")
)
return;
this.$emit("update:outputVariable", this.inputValue);
},
},
}; };
</script> </script>
@ -115,7 +138,7 @@ export default {
} }
.variable-input { .variable-input {
width: 100px; width: 120px;
} }
.output-section :deep(.q-field) { .output-section :deep(.q-field) {

View File

@ -79,7 +79,7 @@
}" }"
class="variable-dropdown prepend-btn" class="variable-dropdown prepend-btn"
size="sm" size="sm"
v-if="variables.length" @click="variables = getCurrentVariables()"
> >
<q-list class="variable-list"> <q-list class="variable-list">
<q-item-label header class="variable-label"> <q-item-label header class="variable-label">
@ -108,6 +108,15 @@
</q-item-section> </q-item-section>
</q-item> </q-item>
</template> </template>
<template v-else>
<q-item>
<q-item-section>
<q-item-label
>无可用变量请先点击命令卡片输出变量按钮设置变量</q-item-label
>
</q-item-section>
</q-item>
</template>
</q-list> </q-list>
</q-btn-dropdown> </q-btn-dropdown>
</template> </template>
@ -176,13 +185,14 @@ export default defineComponent({
emits: ["update:modelValue"], emits: ["update:modelValue"],
setup() { setup() {
const variables = inject("composerVariables", []); const getCurrentVariables = inject("getCurrentVariables");
return { variables }; return { getCurrentVariables };
}, },
data() { data() {
return { return {
selectedVariable: null, selectedVariable: null,
variables: [],
}; };
}, },

View File

@ -136,7 +136,6 @@ export default defineComponent({
id: this.$root.getUniqueId(), id: this.$root.getUniqueId(),
argv: "", argv: "",
saveOutput: false, saveOutput: false,
useOutput: null,
cmd: action.value || action.cmd, cmd: action.value || action.cmd,
value: action.value || action.cmd, value: action.value || action.cmd,
}; };

View File

@ -117,6 +117,9 @@ const reservedWords = [
* @returns {object} - 包含验证结果和错误信息的对象 * @returns {object} - 包含验证结果和错误信息的对象
*/ */
export function validateVariableName(name) { export function validateVariableName(name) {
// 去除空格
name = name.trim();
// 检查是否为空 // 检查是否为空
if (!name) { if (!name) {
return { return {

View File

@ -7,6 +7,8 @@ export const uiCommands = {
value: "quickcommand.showButtonBox", value: "quickcommand.showButtonBox",
label: "按钮组弹窗", label: "按钮组弹窗",
isAsync: true, isAsync: true,
outputVariable: "{id,text}",
saveOutput: true,
config: [ config: [
{ {
label: "按钮组", label: "按钮组",

View File

@ -0,0 +1,177 @@
import { validateVariableName } from "js/common/variableValidator";
/**
* 生成随机后缀
* @param {number} varCount - 当前变量数量
* @returns {string} 随机后缀
*/
function generateRandomSuffix(varCount) {
// 根据变量数量决定后缀长度
let length = 1; // 默认1位
if (varCount >= 100) {
length = 3;
} else if (varCount >= 10) {
length = 2;
}
return (
"_" +
Math.random()
.toString(36)
.substring(2, 2 + length)
);
}
/**
* 解析变量名
* @param {string} value - 变量表达式
* @returns {string[]} 解析出的所有变量名
*/
export function parseVariables(value) {
if (!value) return [];
const destructured = extractDestructuredVars(value);
if (!destructured) {
return [value.trim()];
}
// 处理解构变量
return destructured.vars.map((name) => {
// 处理重命名格式 (key:value)
const parts = name.split(":").map((p) => p.trim());
return parts[parts.length - 1]; // 返回实际的变量名
});
}
/**
* 生成有效的变量名
* @param {string} baseName - 基础变量名
* @param {string[]} existingVars - 已存在的变量列表
* @param {string} [currentName] - 当前的变量名如果有
* @returns {string} 有效的变量名
*/
function generateValidVarName(baseName, existingVars, currentName = null) {
// 如果当前名称有效且不重复,直接返回
if (
currentName &&
validateVariableName(currentName).isValid &&
!existingVars.includes(currentName)
) {
return currentName;
}
// 如果变量名无效,生成一个随机变量名
if (!validateVariableName(baseName).isValid) {
baseName = "var" + generateRandomSuffix(existingVars.length);
}
// 如果变量名重复,添加随机后缀直到不重复
let finalName = baseName;
while (existingVars.includes(finalName)) {
let suffix;
do {
suffix = generateRandomSuffix(existingVars.length);
} while (existingVars.includes(baseName + suffix));
finalName = baseName + suffix;
}
return finalName;
}
/**
* 处理变量更新
* @param {Object} params - 参数对象
* @param {string} params.value - 新的变量名
* @param {string[]} params.existingVars - 当前已存在的变量列表
* @returns {Object} - 处理结果
*/
export function processVariable({ value, existingVars }) {
if (!value) {
return { isValid: true, processedValue: value };
}
const destructured = extractDestructuredVars(value);
if (!destructured) {
// 处理单个变量
const processedVar = generateValidVarName(value, existingVars);
return {
isValid: true,
processedValue: processedVar,
warning:
processedVar !== value ? `变量名已被修改为: ${processedVar}` : null,
};
}
// 处理解构变量
const processedVars = destructured.vars.map((name) => {
const parts = name.split(":").map((p) => p.trim());
const key = parts.length > 1 ? parts[0] : parts[0];
const varName = parts[parts.length - 1];
const processedName = generateValidVarName(varName, existingVars, varName);
return {
key,
processedName,
needsRename: processedName !== varName,
};
});
// 如果有变量需要重命名,使用对象解构格式
if (processedVars.some((v) => v.needsRename)) {
const pairs = processedVars.map((v) =>
v.needsRename ? `${v.key}:${v.processedName}` : v.key
);
const format = `{${pairs.join(", ")}}`;
return {
isValid: true,
processedValue: format,
warning: `变量名已被修改为: ${format}`,
};
}
// 保持原始格式
const format =
destructured.format === "array"
? `[${processedVars.map((v) => v.key).join(", ")}]`
: `{${processedVars.map((v) => v.key).join(", ")}}`;
return {
isValid: true,
processedValue: format,
};
}
/**
* 提取解构变量
* @param {string} value - 输入的变量名
* @returns {Object|null} - 解构的变量数组和格式如果不是解构模式则返回null
*/
export function extractDestructuredVars(value) {
if (!value) return null;
value = value.trim();
// 检查是否是数组解构模式 [a, b, c]
if (value.startsWith("[") && value.endsWith("]")) {
return {
vars: value
.slice(1, -1)
.split(",")
.map((v) => v.trim()),
format: "array",
};
}
// 检查是否是对象解构模式 {a, b, c}
if (value.startsWith("{") && value.endsWith("}")) {
return {
vars: value
.slice(1, -1)
.split(",")
.map((v) => v.trim()),
format: "object",
};
}
return null;
}