增加单个上传和多个上传

This commit is contained in:
2026-05-19 14:52:17 +08:00
parent f64d6334e2
commit 727e9fa9c0

View File

@@ -95,6 +95,64 @@
/>
<!-- 开关 -->
<el-switch v-else-if="fieldItem.type === 'switch'" v-model="dynamicFormValues[fieldItem.field]" />
<!-- 单个文件上传 -->
<div v-else-if="fieldItem.type === 'upload'" class="field-upload-wrapper">
<el-upload
:key="`field-upload-${fieldItem.field}-${getFieldUploadKey(fieldItem.field)}`"
:auto-upload="false"
:on-change="(file: any) => handleFieldUpload(fieldItem.field, file, 'upload')"
:file-list="[]"
:limit="1"
:show-file-list="false"
class="field-upload"
>
<el-button size="small" type="primary" :disabled="getFieldFileList(fieldItem.field).length >= 1">
选择文件
</el-button>
</el-upload>
<!-- 手动显示已上传成功的文件列表 -->
<div v-if="getFieldFileList(fieldItem.field).length > 0" class="uploaded-files-list">
<div v-for="(uploadedFile, fileIdx) in getFieldFileList(fieldItem.field)" :key="fileIdx" class="uploaded-file-item">
<span class="file-name">{{ uploadedFile.name }}</span>
<el-button
type="danger"
link
size="small"
@click="removeFieldFile(fieldItem.field, fileIdx, 'upload')"
>
删除
</el-button>
</div>
</div>
</div>
<!-- 多个文件上传 -->
<div v-else-if="fieldItem.type === 'uploadMultiple'" class="field-upload-wrapper">
<el-upload
:key="`field-upload-${fieldItem.field}-${getFieldUploadKey(fieldItem.field)}`"
:auto-upload="false"
:multiple="true"
:on-change="(file: any) => handleFieldUpload(fieldItem.field, file, 'uploadMultiple')"
:file-list="[]"
:show-file-list="false"
class="field-upload"
>
<el-button size="small" type="primary">选择文件</el-button>
</el-upload>
<!-- 手动显示已上传成功的文件列表 -->
<div v-if="getFieldFileList(fieldItem.field).length > 0" class="uploaded-files-list">
<div v-for="(uploadedFile, fileIdx) in getFieldFileList(fieldItem.field)" :key="fileIdx" class="uploaded-file-item">
<span class="file-name">{{ uploadedFile.name }}</span>
<el-button
type="danger"
link
size="small"
@click="removeFieldFile(fieldItem.field, fileIdx, 'uploadMultiple')"
>
删除
</el-button>
</div>
</div>
</div>
<!-- 键值对输入HTTP节点的headers和body -->
<div v-else-if="fieldItem.type === 'keyValue'" class="key-value-input-wrapper">
<div
@@ -199,11 +257,50 @@
<el-option label="数字" value="number" />
<el-option label="多行文本" value="textarea" />
<el-option label="开关" value="switch" />
<el-option label="单个上传" value="upload" />
<el-option label="多个上传" value="uploadMultiple" />
</el-select>
<el-checkbox v-model="customField.required" class="custom-field-required">必填</el-checkbox>
<el-button type="danger" link @click="removeCustomField(index)">删除</el-button>
</div>
<el-input v-model="customField.value" placeholder="默认值" class="custom-field-value-full" />
<!-- 文件上传类型显示上传组件 -->
<div v-if="customField.type === 'upload' || customField.type === 'uploadMultiple'" class="custom-field-upload-wrapper">
<el-upload
:key="`custom-upload-${index}-${customField.uploadKey || 0}`"
:auto-upload="false"
:multiple="customField.type === 'uploadMultiple'"
:on-change="(file: any) => handleCustomFieldUpload(index, file, customField.type)"
:file-list="[]"
:limit="customField.type === 'upload' ? 1 : undefined"
:show-file-list="false"
class="custom-field-upload"
>
<el-button size="small" type="primary" :disabled="customField.type === 'upload' && getCustomFieldFileList(index).length >= 1">
选择文件
</el-button>
</el-upload>
<!-- 手动显示已上传成功的文件列表 -->
<div v-if="getCustomFieldFileList(index).length > 0" class="uploaded-files-list">
<div v-for="(uploadedFile, fileIdx) in getCustomFieldFileList(index)" :key="fileIdx" class="uploaded-file-item">
<span class="file-name">{{ uploadedFile.name }}</span>
<el-button
type="danger"
link
size="small"
@click="removeCustomFieldFile(index, fileIdx, customField.type)"
>
删除
</el-button>
</div>
</div>
</div>
<!-- 非文件上传类型显示默认值输入框 -->
<el-input
v-else
v-model="customField.value"
placeholder="默认值"
class="custom-field-value-full"
/>
</div>
<el-button type="primary" link class="w100" @click="addCustomField">+ 添加自定义字段</el-button>
</template>
@@ -1718,12 +1815,108 @@ const getSelectOptions = (fieldItem: NodeLibraryFormItem) => {
};
// 添加自定义字段
const addCustomField = () => {
customFields.value.push({ label: '', value: '', type: 'input', required: false });
customFields.value.push({ label: '', value: '', type: 'input', required: false, fileList: [], uploadKey: 0 });
};
// 删除自定义字段
const removeCustomField = (index: number) => {
customFields.value.splice(index, 1);
};
// 获取自定义字段的文件列表
const getCustomFieldFileList = (index: number) => {
const field = customFields.value[index];
if (!field || !field.fileList) return [];
return field.fileList;
};
// 处理自定义字段文件上传
const handleCustomFieldUpload = async (index: number, file: any, type: string) => {
const field = customFields.value[index];
if (!field) return;
try {
// 上传文件到OSS
const uploadRes = await uploadFile(file.raw);
// 检查上传是否成功
if (!uploadRes || !uploadRes.data || !uploadRes.data.fileURL) {
throw new Error('上传失败未返回文件URL');
}
const fileUrl = uploadRes.data.fileAddressPrefix
? `${uploadRes.data.fileAddressPrefix}${uploadRes.data.fileURL}`
: uploadRes.data.fileURL;
// 初始化 fileList
if (!field.fileList) {
field.fileList = [];
}
// 根据类型处理
if (type === 'upload') {
// 单个上传:替换现有文件
field.fileList = [{ name: file.name, url: fileUrl }];
field.value = fileUrl;
} else if (type === 'uploadMultiple') {
// 多个上传:添加到数组
field.fileList.push({ name: file.name, url: fileUrl });
// 解析现有的 value
let urls: string[] = [];
if (field.value) {
try {
urls = JSON.parse(field.value);
if (!Array.isArray(urls)) {
urls = [field.value];
}
} catch (e) {
urls = field.value ? [field.value] : [];
}
}
// 添加新的 URL
urls.push(fileUrl);
field.value = JSON.stringify(urls);
}
ElMessage.success('文件上传成功');
} catch (error: any) {
ElMessage.error(error?.message || '文件上传失败');
console.error('Upload error:', error);
// 上传失败时,递增 uploadKey 来重置上传组件
field.uploadKey = (field.uploadKey || 0) + 1;
}
};
// 删除自定义字段的文件
const removeCustomFieldFile = (index: number, fileIdx: number, type: string) => {
const field = customFields.value[index];
if (!field || !field.fileList) return;
// 删除文件
field.fileList.splice(fileIdx, 1);
// 更新 value
if (type === 'upload') {
// 单个上传:清空 value
field.value = '';
} else if (type === 'uploadMultiple') {
// 多个上传:从数组中删除对应的 URL
try {
let urls: string[] = [];
if (field.value) {
urls = JSON.parse(field.value);
if (!Array.isArray(urls)) {
urls = [];
}
}
urls.splice(fileIdx, 1);
field.value = urls.length > 0 ? JSON.stringify(urls) : '';
} catch (e) {
field.value = '';
}
}
// 递增 uploadKey 来重置上传组件
field.uploadKey = (field.uploadKey || 0) + 1;
};
// 获取键值对数组
const getKeyValuePairs = (field: string) => {
const value = dynamicFormValues[field];
@@ -1789,6 +1982,104 @@ const updateKeyValueFieldFromPairs = (field: string, pairs: Array<{ key: string;
// 保存为JSON字符串
dynamicFormValues[field] = Object.keys(obj).length > 0 ? JSON.stringify(obj) : '';
};
// 存储字段的文件列表
const fieldFileLists = reactive<Record<string, any[]>>({});
// 存储字段的上传key用于重置上传组件
const fieldUploadKeys = reactive<Record<string, number>>({});
// 获取字段的文件列表
const getFieldFileList = (field: string) => {
return fieldFileLists[field] || [];
};
// 获取字段的上传key
const getFieldUploadKey = (field: string) => {
return fieldUploadKeys[field] || 0;
};
// 处理字段文件上传
const handleFieldUpload = async (field: string, file: any, type: string) => {
try {
// 上传文件到OSS
const uploadRes = await uploadFile(file.raw);
// 检查上传是否成功
if (!uploadRes || !uploadRes.data || !uploadRes.data.fileURL) {
throw new Error('上传失败未返回文件URL');
}
const fileUrl = uploadRes.data.fileAddressPrefix
? `${uploadRes.data.fileAddressPrefix}${uploadRes.data.fileURL}`
: uploadRes.data.fileURL;
// 初始化文件列表
if (!fieldFileLists[field]) {
fieldFileLists[field] = [];
}
// 根据类型处理
if (type === 'upload') {
// 单个上传:替换现有文件
fieldFileLists[field] = [{ name: file.name, url: fileUrl }];
dynamicFormValues[field] = fileUrl;
} else if (type === 'uploadMultiple') {
// 多个上传:添加到数组
fieldFileLists[field].push({ name: file.name, url: fileUrl });
// 解析现有的 value
let urls: string[] = [];
if (dynamicFormValues[field]) {
try {
urls = JSON.parse(dynamicFormValues[field]);
if (!Array.isArray(urls)) {
urls = [dynamicFormValues[field]];
}
} catch (e) {
urls = dynamicFormValues[field] ? [dynamicFormValues[field]] : [];
}
}
// 添加新的 URL
urls.push(fileUrl);
dynamicFormValues[field] = JSON.stringify(urls);
}
ElMessage.success('文件上传成功');
} catch (error: any) {
ElMessage.error(error?.message || '文件上传失败');
console.error('Upload error:', error);
// 上传失败时,递增 uploadKey 来重置上传组件
fieldUploadKeys[field] = (fieldUploadKeys[field] || 0) + 1;
}
};
// 删除字段的文件
const removeFieldFile = (field: string, fileIdx: number, type: string) => {
if (!fieldFileLists[field]) return;
// 删除文件
fieldFileLists[field].splice(fileIdx, 1);
// 更新 value
if (type === 'upload') {
// 单个上传:清空 value
dynamicFormValues[field] = '';
} else if (type === 'uploadMultiple') {
// 多个上传:从数组中删除对应的 URL
try {
let urls: string[] = [];
if (dynamicFormValues[field]) {
urls = JSON.parse(dynamicFormValues[field]);
if (!Array.isArray(urls)) {
urls = [];
}
}
urls.splice(fileIdx, 1);
dynamicFormValues[field] = urls.length > 0 ? JSON.stringify(urls) : '';
} catch (e) {
dynamicFormValues[field] = '';
}
}
// 递增 uploadKey 来重置上传组件
fieldUploadKeys[field] = (fieldUploadKeys[field] || 0) + 1;
};
// 判断是否可以添加自定义字段排除判断节点、开始节点、HTTP节点等
const canAddCustomFields = (element: SelectedState | null) => {
if (!element || element.kind !== 'node') return false;
@@ -3149,6 +3440,44 @@ onBeforeUnmount(() => {
align-self: flex-start;
margin-top: 4px;
}
.custom-field-upload-wrapper,
.field-upload-wrapper {
display: flex;
flex-direction: column;
gap: 8px;
}
.uploaded-files-list {
display: flex;
flex-direction: column;
gap: 6px;
padding: 8px;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 6px;
}
.uploaded-file-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 10px;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 4px;
transition: all 0.2s ease;
}
.uploaded-file-item:hover {
border-color: #cbd5e1;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
.uploaded-file-item .file-name {
flex: 1;
font-size: 13px;
color: #374151;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 12px;
}
.input-source-list {
display: flex;
flex-direction: column;