Files
Agent-n8n/n8n-n8n-1.109.2/packages/frontend/editor-ui/src/components/VariablesForm.vue
2025-09-08 04:48:28 +08:00

128 lines
3.1 KiB
Vue
Executable File

<script lang="ts" setup>
import type { EnvironmentVariable, Rule, RuleGroup } from '@/Interface';
import { useI18n } from '@n8n/i18n';
import { N8nButton, N8nFormInput } from '@n8n/design-system';
import { computed, reactive, ref, toRaw } from 'vue';
import VariablesUsageBadge from './VariablesUsageBadge.vue';
const props = defineProps<{
variable: EnvironmentVariable;
}>();
const emit = defineEmits<{
submit: [variable: EnvironmentVariable];
cancel: [];
}>();
const i18n = useI18n();
const keyValidationRules: Array<Rule | RuleGroup> = [
{ name: 'REQUIRED' },
{ name: 'MAX_LENGTH', config: { maximum: 50 } },
{
name: 'MATCH_REGEX',
config: {
regex: /^[a-zA-Z]/,
message: i18n.baseText('variables.editing.key.error.startsWithLetter'),
},
},
{
name: 'MATCH_REGEX',
config: {
regex: /^[a-zA-Z][a-zA-Z0-9_]*$/,
message: i18n.baseText('variables.editing.key.error.jsonKey'),
},
},
];
const VALUE_MAX_LENGTH = 220;
const valueValidationRules: Array<Rule | RuleGroup> = [
{ name: 'MAX_LENGTH', config: { maximum: VALUE_MAX_LENGTH } },
];
const form = reactive<EnvironmentVariable>(structuredClone(toRaw(props.variable)));
const formValidation = reactive<{
key: boolean;
value: boolean;
}>({
key: false,
value: false,
});
const isValid = computed(() => Object.values(formValidation).every((value) => value));
const handleCancel = () => emit('cancel');
const validateOnBlur = ref(false);
const handleSubmit = () => {
validateOnBlur.value = true;
if (isValid.value) {
emit('submit', form);
}
};
</script>
<template>
<tr>
<td class="key-cell">
<N8nFormInput
v-model="form.key"
label=""
name="key"
data-test-id="variable-row-key-input"
:placeholder="i18n.baseText('variables.editing.key.placeholder')"
required
:validate-on-blur="validateOnBlur"
:validation-rules="keyValidationRules"
focus-initially
@validate="(value: boolean) => (formValidation.key = value)"
/>
</td>
<td class="value-cell" width="100%">
<N8nFormInput
v-model="form.value"
class="key-input"
label=""
name="value"
data-test-id="variable-row-value-input"
:placeholder="i18n.baseText('variables.editing.value.placeholder')"
type="textarea"
:autosize="{ minRows: 1, maxRows: 6 }"
size="medium"
:maxlength="VALUE_MAX_LENGTH"
:validate-on-blur="validateOnBlur"
:validation-rules="valueValidationRules"
@validate="(value: boolean) => (formValidation.value = value)"
/>
</td>
<td><VariablesUsageBadge v-if="formValidation.key" :name="form.key" /></td>
<td align="right">
<N8nButton
data-test-id="variable-row-cancel-button"
type="tertiary"
class="mr-xs"
@click="handleCancel"
>
{{ i18n.baseText('variables.row.button.cancel') }}
</N8nButton>
<N8nButton data-test-id="variable-row-save-button" type="primary" @click="handleSubmit">
{{ i18n.baseText('variables.row.button.save') }}
</N8nButton>
</td>
</tr>
</template>
<style lang="scss" scoped>
.value-cell {
width: 100%;
max-width: 50%;
}
.key-input {
:deep(textarea) {
min-height: 40px !important;
resize: none;
}
}
</style>