|
|
|
|
@ -278,7 +278,8 @@
|
|
|
|
|
:on-error="(err) => handleFileUploadError(field.key, err)"
|
|
|
|
|
:on-remove="(file) => handleFileRemove(field.key, file)"
|
|
|
|
|
:before-upload="beforeUpload"
|
|
|
|
|
:limit="1"
|
|
|
|
|
multiple
|
|
|
|
|
:limit="10"
|
|
|
|
|
class="file-upload-component"
|
|
|
|
|
>
|
|
|
|
|
<el-button type="primary">
|
|
|
|
|
@ -287,7 +288,7 @@
|
|
|
|
|
</el-button>
|
|
|
|
|
<template #tip>
|
|
|
|
|
<div class="el-upload__tip">
|
|
|
|
|
支持上传单个文件,文件大小不超过10MB
|
|
|
|
|
支持上传多个文件,单个文件大小不超过10MB
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-upload>
|
|
|
|
|
@ -3419,8 +3420,8 @@ const initializeFormDataFromTemplate = (config) => {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
} else if (field.element_type === 'file') {
|
|
|
|
|
// 文件字段初始化为空字符串
|
|
|
|
|
formData.value[field.key] = ''
|
|
|
|
|
// 文件字段初始化为空数组(支持多附件)
|
|
|
|
|
formData.value[field.key] = []
|
|
|
|
|
} else if (field.element_type === 'meeting_minutes') {
|
|
|
|
|
// 会议纪要类型初始化为 null
|
|
|
|
|
formData.value[field.key] = null
|
|
|
|
|
@ -3457,8 +3458,8 @@ const initializeFormDataFromTemplate = (config) => {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
} else if (field.element_type === 'file') {
|
|
|
|
|
// 文件字段初始化为空字符串
|
|
|
|
|
formData.value[field.key] = ''
|
|
|
|
|
// 文件字段初始化为空数组(支持多附件)
|
|
|
|
|
formData.value[field.key] = []
|
|
|
|
|
} else if (field.element_type === 'meeting_minutes') {
|
|
|
|
|
// 会议纪要类型初始化为 null
|
|
|
|
|
formData.value[field.key] = null
|
|
|
|
|
@ -3624,56 +3625,89 @@ const normalizeChecklistData = () => {
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取文件列表(用于 el-upload 组件显示)
|
|
|
|
|
const getFileList = (fieldKey) => {
|
|
|
|
|
const value = formData.value[fieldKey]
|
|
|
|
|
// 将各种历史格式统一为附件列表(用于编辑、回显、提交)
|
|
|
|
|
const normalizeFileUploadValue = (value) => {
|
|
|
|
|
if (!value) return []
|
|
|
|
|
|
|
|
|
|
// 如果已经是数组格式(el-upload 需要的格式)
|
|
|
|
|
|
|
|
|
|
const normalizeItem = (raw, idx = 0) => {
|
|
|
|
|
if (!raw) return null
|
|
|
|
|
|
|
|
|
|
if (typeof raw === 'string') {
|
|
|
|
|
const s = raw.trim()
|
|
|
|
|
if (!s) return null
|
|
|
|
|
const fileName = s.split('/').pop() || `附件${idx + 1}`
|
|
|
|
|
return {
|
|
|
|
|
uid: `legacy-${Date.now()}-${idx}`,
|
|
|
|
|
name: fileName,
|
|
|
|
|
url: s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof raw === 'object') {
|
|
|
|
|
const id = raw.id ?? raw.uid ?? `legacy-${Date.now()}-${idx}`
|
|
|
|
|
const name = raw.original_name || raw.file_name || raw.name || `附件${idx + 1}`
|
|
|
|
|
const filePath = raw.file_path || (raw.folder && raw.name ? `${raw.folder}/${raw.name}` : '')
|
|
|
|
|
const url = raw.url || raw.path || raw.file_url || filePath || ''
|
|
|
|
|
return {
|
|
|
|
|
uid: id,
|
|
|
|
|
name,
|
|
|
|
|
url,
|
|
|
|
|
response: raw
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
return value
|
|
|
|
|
return value.map((item, idx) => normalizeItem(item, idx)).filter(Boolean)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果是字符串,尝试解析为 JSON
|
|
|
|
|
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(value)
|
|
|
|
|
// 如果是对象,转换为 el-upload 需要的格式
|
|
|
|
|
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
|
|
|
return [{
|
|
|
|
|
name: parsed.original_name || parsed.name || '文件',
|
|
|
|
|
url: parsed.url || (parsed.folder && parsed.name ? `${parsed.folder}/${parsed.name}` : ''),
|
|
|
|
|
uid: parsed.id || Date.now(),
|
|
|
|
|
response: parsed // 保存完整的后端数据
|
|
|
|
|
}]
|
|
|
|
|
}
|
|
|
|
|
// 如果解析后是数组,直接返回
|
|
|
|
|
if (Array.isArray(parsed)) {
|
|
|
|
|
return parsed
|
|
|
|
|
return parsed.map((item, idx) => normalizeItem(item, idx)).filter(Boolean)
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// 解析失败,可能是文件路径字符串
|
|
|
|
|
// 尝试从路径中提取文件名
|
|
|
|
|
const fileName = value.split('/').pop() || value
|
|
|
|
|
return [{
|
|
|
|
|
name: fileName,
|
|
|
|
|
url: value,
|
|
|
|
|
uid: Date.now()
|
|
|
|
|
}]
|
|
|
|
|
const one = normalizeItem(parsed, 0)
|
|
|
|
|
return one ? [one] : []
|
|
|
|
|
} catch (_) {
|
|
|
|
|
const one = normalizeItem(value, 0)
|
|
|
|
|
return one ? [one] : []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 如果是对象,转换为 el-upload 需要的格式
|
|
|
|
|
if (value && typeof value === 'object') {
|
|
|
|
|
return [{
|
|
|
|
|
name: value.original_name || value.name || '文件',
|
|
|
|
|
url: value.url || (value.folder && value.name ? `${value.folder}/${value.name}` : ''),
|
|
|
|
|
uid: value.id || Date.now(),
|
|
|
|
|
response: value // 保存完整的后端数据
|
|
|
|
|
}]
|
|
|
|
|
|
|
|
|
|
const one = normalizeItem(value, 0)
|
|
|
|
|
return one ? [one] : []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 规范化模板中 file 字段,确保 formData 始终是附件列表
|
|
|
|
|
const normalizeFileUploadData = () => {
|
|
|
|
|
if (!templateConfig.value) return
|
|
|
|
|
|
|
|
|
|
const normalizeField = (field) => {
|
|
|
|
|
if (field?.element_type !== 'file' || !field.key) return
|
|
|
|
|
formData.value[field.key] = normalizeFileUploadValue(formData.value[field.key])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
Object.keys(templateConfig.value).forEach(sectionKey => {
|
|
|
|
|
const section = templateConfig.value[sectionKey]
|
|
|
|
|
if (section.fields && Array.isArray(section.fields)) {
|
|
|
|
|
section.fields.forEach(normalizeField)
|
|
|
|
|
}
|
|
|
|
|
if (section.rounds && Array.isArray(section.rounds)) {
|
|
|
|
|
section.rounds.forEach(round => {
|
|
|
|
|
if (round.fields && Array.isArray(round.fields)) {
|
|
|
|
|
round.fields.forEach(normalizeField)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取文件列表(用于 el-upload 组件显示)
|
|
|
|
|
const getFileList = (fieldKey) => {
|
|
|
|
|
return normalizeFileUploadValue(formData.value[fieldKey])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 文件上传成功处理
|
|
|
|
|
@ -3698,8 +3732,22 @@ const handleFileUploadSuccess = (fieldKey, response) => {
|
|
|
|
|
file_path: fileData.folder && fileData.name ? `${fileData.folder}/${fileData.name}` : null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存为 JSON 字符串(便于存储和传输)
|
|
|
|
|
formData.value[fieldKey] = JSON.stringify(fileInfo)
|
|
|
|
|
// 统一存储为数组,便于多附件回显、二次编辑和后续展示
|
|
|
|
|
const currentFiles = normalizeFileUploadValue(formData.value[fieldKey])
|
|
|
|
|
const exists = currentFiles.some(item => {
|
|
|
|
|
const idA = item?.response?.id ?? item?.uid
|
|
|
|
|
const idB = fileInfo.id
|
|
|
|
|
return idA && idB && String(idA) === String(idB)
|
|
|
|
|
})
|
|
|
|
|
if (!exists) {
|
|
|
|
|
currentFiles.push({
|
|
|
|
|
uid: fileInfo.id || `upload-${Date.now()}`,
|
|
|
|
|
name: fileInfo.original_name || fileInfo.name || '文件',
|
|
|
|
|
url: fileInfo.file_path || '',
|
|
|
|
|
response: fileInfo
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
formData.value[fieldKey] = currentFiles
|
|
|
|
|
|
|
|
|
|
ElMessage.success('上传成功')
|
|
|
|
|
} else {
|
|
|
|
|
@ -3712,8 +3760,15 @@ const handleFileUploadSuccess = (fieldKey, response) => {
|
|
|
|
|
|
|
|
|
|
// 文件移除处理
|
|
|
|
|
const handleFileRemove = (fieldKey, file) => {
|
|
|
|
|
// 清空该字段的值
|
|
|
|
|
formData.value[fieldKey] = ''
|
|
|
|
|
const currentFiles = normalizeFileUploadValue(formData.value[fieldKey])
|
|
|
|
|
const removedUid = file?.uid
|
|
|
|
|
const removedName = file?.name
|
|
|
|
|
const nextFiles = currentFiles.filter(item => {
|
|
|
|
|
if (removedUid && String(item.uid) === String(removedUid)) return false
|
|
|
|
|
if (removedName && item.name === removedName && item.url === file?.url) return false
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
formData.value[fieldKey] = nextFiles
|
|
|
|
|
ElMessage.success('已移除文件')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -3935,18 +3990,9 @@ const formatFieldValue = (value, elementType) => {
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
if (elementType === 'file') {
|
|
|
|
|
// 文件字段:尝试解析 JSON 字符串获取文件名
|
|
|
|
|
try {
|
|
|
|
|
const fileInfo = typeof value === 'string' ? JSON.parse(value) : value
|
|
|
|
|
if (fileInfo && typeof fileInfo === 'object') {
|
|
|
|
|
return fileInfo.original_name || fileInfo.name || '文件'
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// 解析失败,可能是文件路径字符串
|
|
|
|
|
const fileName = value.split('/').pop() || value
|
|
|
|
|
return fileName
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
const files = normalizeFileUploadValue(value)
|
|
|
|
|
if (files.length === 0) return '-'
|
|
|
|
|
return files.map(file => file.name || '文件').join(',')
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
@ -3964,16 +4010,7 @@ const formatFieldValueByType = (value, type) => {
|
|
|
|
|
case 'number':
|
|
|
|
|
return typeof value === 'number' ? value.toLocaleString('zh-CN') : value
|
|
|
|
|
case 'file':
|
|
|
|
|
// 如果是 JSON 字符串,解析后显示文件名
|
|
|
|
|
try {
|
|
|
|
|
const files = JSON.parse(value)
|
|
|
|
|
if (Array.isArray(files)) {
|
|
|
|
|
return files.map(f => f.name || f).join(', ')
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
return normalizeFileUploadValue(value).map(f => f.name || '文件').join(', ') || '-'
|
|
|
|
|
default:
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
@ -6738,9 +6775,10 @@ const loadDraftData = async (id) => {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
// 规范化勾选清单数据格式(确保数组格式正确,补充缺失的备注字段)
|
|
|
|
|
// 规范化勾选清单与文件上传数据(兼容旧值,确保可二次编辑)
|
|
|
|
|
await nextTick()
|
|
|
|
|
normalizeChecklistData()
|
|
|
|
|
normalizeFileUploadData()
|
|
|
|
|
|
|
|
|
|
// related_contract 已移除,无需恢复合同字段
|
|
|
|
|
}
|
|
|
|
|
|