mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
260 lines
5.9 KiB
Vue
260 lines
5.9 KiB
Vue
<script setup lang="ts">
|
|
import { ref, watch, computed } from 'vue';
|
|
import get from 'lodash/get';
|
|
import type { INodeParameters, INodeProperties } from 'n8n-workflow';
|
|
import { deepCopy } from 'n8n-workflow';
|
|
|
|
import { useI18n } from '@n8n/i18n';
|
|
import type { IUpdateInformation } from '@/Interface';
|
|
import CollectionParameter from '@/components/CollectionParameter.vue';
|
|
import ParameterInputFull from '@/components/ParameterInputFull.vue';
|
|
import { N8nButton, N8nInputLabel, N8nText } from '@n8n/design-system';
|
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
|
import { useNDVStore } from '@/stores/ndv.store';
|
|
import { storeToRefs } from 'pinia';
|
|
|
|
defineOptions({ name: 'MultipleParameter' });
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
nodeValues: INodeParameters;
|
|
parameter: INodeProperties;
|
|
path: string;
|
|
values?: INodeParameters[];
|
|
isReadOnly?: boolean;
|
|
}>(),
|
|
{
|
|
values: () => [] as INodeParameters[],
|
|
isReadOnly: false,
|
|
},
|
|
);
|
|
|
|
const emit = defineEmits<{
|
|
valueChanged: [parameterData: IUpdateInformation];
|
|
}>();
|
|
|
|
const ndvStore = useNDVStore();
|
|
const i18n = useI18n();
|
|
|
|
const { activeNode } = storeToRefs(ndvStore);
|
|
|
|
const mutableValues = ref<INodeParameters[]>(deepCopy(props.values));
|
|
|
|
watch(
|
|
() => props.values,
|
|
(newValues) => {
|
|
mutableValues.value = deepCopy(newValues);
|
|
},
|
|
{ deep: true },
|
|
);
|
|
|
|
const addButtonText = computed(() => {
|
|
if (!props.parameter.typeOptions?.multipleValueButtonText) {
|
|
return i18n.baseText('multipleParameter.addItem');
|
|
}
|
|
|
|
return i18n.nodeText(activeNode.value?.type).multipleValueButtonText(props.parameter);
|
|
});
|
|
|
|
const hideDelete = computed(() => props.parameter.options?.length === 1);
|
|
|
|
const sortable = computed(() => !!props.parameter.typeOptions?.sortable);
|
|
|
|
const addItem = () => {
|
|
const name = getPath();
|
|
const currentValue = get(props.nodeValues, name, []) as INodeParameters[];
|
|
|
|
currentValue.push(deepCopy(props.parameter.default as INodeParameters));
|
|
|
|
const parameterData = {
|
|
name,
|
|
value: currentValue,
|
|
};
|
|
|
|
emit('valueChanged', parameterData);
|
|
};
|
|
|
|
const deleteItem = (index: number) => {
|
|
const parameterData = {
|
|
name: getPath(index),
|
|
value: undefined,
|
|
};
|
|
|
|
emit('valueChanged', parameterData);
|
|
};
|
|
|
|
const getPath = (index?: number) => {
|
|
return props.path + (index !== undefined ? `[${index}]` : '');
|
|
};
|
|
|
|
const moveOptionDown = (index: number) => {
|
|
mutableValues.value.splice(index + 1, 0, mutableValues.value.splice(index, 1)[0]);
|
|
|
|
emit('valueChanged', {
|
|
name: props.path,
|
|
value: mutableValues.value,
|
|
});
|
|
};
|
|
|
|
const moveOptionUp = (index: number) => {
|
|
mutableValues.value.splice(index - 1, 0, mutableValues.value.splice(index, 1)[0]);
|
|
|
|
emit('valueChanged', {
|
|
name: props.path,
|
|
value: mutableValues.value,
|
|
});
|
|
};
|
|
|
|
const valueChanged = (parameterData: IUpdateInformation) => {
|
|
emit('valueChanged', parameterData);
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div class="duplicate-parameter" @keydown.stop>
|
|
<N8nInputLabel
|
|
:label="i18n.nodeText(activeNode?.type).inputLabelDisplayName(parameter, path)"
|
|
:tooltip-text="i18n.nodeText(activeNode?.type).inputLabelDescription(parameter, path)"
|
|
:underline="true"
|
|
size="small"
|
|
color="text-dark"
|
|
/>
|
|
|
|
<div
|
|
v-for="(value, index) in mutableValues"
|
|
:key="index"
|
|
class="duplicate-parameter-item"
|
|
:class="parameter.type"
|
|
>
|
|
<div v-if="!isReadOnly" class="delete-item clickable">
|
|
<FontAwesomeIcon
|
|
icon="trash"
|
|
:title="i18n.baseText('multipleParameter.deleteItem')"
|
|
@click="deleteItem(index)"
|
|
/>
|
|
<div v-if="sortable">
|
|
<FontAwesomeIcon
|
|
v-if="index !== 0"
|
|
icon="angle-up"
|
|
class="clickable"
|
|
:title="i18n.baseText('multipleParameter.moveUp')"
|
|
@click="moveOptionUp(index)"
|
|
/>
|
|
<FontAwesomeIcon
|
|
v-if="index !== mutableValues.length - 1"
|
|
icon="angle-down"
|
|
class="clickable"
|
|
:title="i18n.baseText('multipleParameter.moveDown')"
|
|
@click="moveOptionDown(index)"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div v-if="parameter.type === 'collection'">
|
|
<CollectionParameter
|
|
:parameter="parameter"
|
|
:values="value"
|
|
:node-values="nodeValues"
|
|
:path="getPath(index)"
|
|
:hide-delete="hideDelete"
|
|
:is-read-only="isReadOnly"
|
|
@value-changed="valueChanged"
|
|
/>
|
|
</div>
|
|
<div v-else>
|
|
<ParameterInputFull
|
|
class="duplicate-parameter-input-item"
|
|
:parameter="parameter"
|
|
:value="value"
|
|
:display-options="true"
|
|
:hide-label="true"
|
|
:path="getPath(index)"
|
|
input-size="small"
|
|
:is-read-only="isReadOnly"
|
|
@update="valueChanged"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="add-item-wrapper">
|
|
<div
|
|
v-if="(mutableValues && mutableValues.length === 0) || isReadOnly"
|
|
class="no-items-exist"
|
|
>
|
|
<N8nText size="small">{{
|
|
i18n.baseText('multipleParameter.currentlyNoItemsExist')
|
|
}}</N8nText>
|
|
</div>
|
|
<N8nButton
|
|
v-if="!isReadOnly"
|
|
type="tertiary"
|
|
block
|
|
:label="addButtonText"
|
|
@click="addItem()"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.duplicate-parameter {
|
|
:deep(.button) {
|
|
--button-background-color: var(--color-background-base);
|
|
--button-border-color: var(--color-foreground-base);
|
|
}
|
|
|
|
:deep(.duplicate-parameter-item) {
|
|
position: relative;
|
|
|
|
.multi > .delete-item {
|
|
top: 0.1em;
|
|
}
|
|
}
|
|
|
|
:deep(.duplicate-parameter-input-item) {
|
|
margin: 0.5em 0 0.25em 2em;
|
|
}
|
|
|
|
:deep(.duplicate-parameter-item + .duplicate-parameter-item) {
|
|
.collection-parameter-wrapper {
|
|
border-top: 1px dashed #999;
|
|
margin-top: var(--spacing-xs);
|
|
}
|
|
}
|
|
}
|
|
|
|
.duplicate-parameter-item {
|
|
~ .add-item-wrapper {
|
|
margin-top: var(--spacing-xs);
|
|
}
|
|
}
|
|
|
|
.delete-item {
|
|
display: none;
|
|
position: absolute;
|
|
left: 0.1em;
|
|
top: 0.3em;
|
|
z-index: 999;
|
|
color: #f56c6c;
|
|
width: 15px;
|
|
font-size: var(--font-size-2xs);
|
|
|
|
:hover {
|
|
color: #ff0000;
|
|
}
|
|
}
|
|
|
|
.no-items-exist {
|
|
margin: var(--spacing-xs) 0;
|
|
}
|
|
</style>
|
|
|
|
<style>
|
|
.duplicate-parameter-item:hover > .delete-item {
|
|
display: inline;
|
|
}
|
|
|
|
.duplicate-parameter-item .multi > .delete-item {
|
|
top: 0.1em;
|
|
}
|
|
</style>
|