【优化】文件上传组件支持文件名称正常回显及多职位选不到的优化

This commit is contained in:
俞宝山
2026-04-23 15:56:27 +08:00
parent 08770a559f
commit e459a33390
5 changed files with 188 additions and 65 deletions
+4
View File
@@ -80,6 +80,10 @@ export default {
fileGetFileListByUrlList(data) {
return request('getFileListByUrlList', data)
},
// 根据文件id集合获取文件集合
fileGetFileListByIds(data) {
return request('getFileListByIds', data)
},
// 删除文件
fileDelete(data) {
return request('delete', data)
+128 -52
View File
@@ -4,6 +4,7 @@
v-if="props.uploadMode === 'file'"
v-model:file-list="fileList"
name="file"
:multiple="props.uploadNumber > 1"
:action="action"
:headers="headers"
:maxCount="props.uploadNumber"
@@ -23,6 +24,7 @@
v-if="props.uploadMode === 'video'"
v-model:file-list="fileList"
name="file"
:multiple="props.uploadNumber > 1"
:action="action"
:headers="headers"
:maxCount="props.uploadNumber"
@@ -45,6 +47,7 @@
v-if="props.uploadMode === 'image'"
v-model:file-list="fileList"
name="file"
:multiple="props.uploadNumber > 1"
:action="action"
:headers="headers"
:maxCount="props.uploadNumber"
@@ -84,7 +87,7 @@
v-if="props.uploadMode === 'drag'"
v-model:fileList="fileList"
name="file"
:multiple="true"
:multiple="props.uploadNumber > 1"
:action="action"
:headers="headers"
:maxCount="props.uploadNumber"
@@ -108,6 +111,7 @@
<script setup name="uploadIndex">
import tool from '@/utils/tool'
import fileApi from '@/api/dev/fileApi'
import { convertUrl } from '@/utils/apiAdaptive'
import { message, Upload } from 'ant-design-vue'
import { cloneDeep } from 'lodash-es'
@@ -205,9 +209,25 @@
// 构造文件对象
const buildFileObject = (url, id) => {
let name = id
if (url) {
// 提取最后一段路径
name = url.substring(url.lastIndexOf('/') + 1)
// 去除查询参数
if (name.includes('?')) {
name = name.split('?')[0]
}
// 特殊处理:如果是 download 接口且带 id 参数,尝试提取 id 作为备选名称
if ((name === 'download' || !name) && url.includes('id=')) {
const match = url.match(/[?&]id=([^&]+)/)
if (match) {
name = match[1]
}
}
}
return {
data: url ? url : '/api' + props.uploadIdDownloadUrl + id,
name: url ? url : id,
name: name,
url: url ? url : '/api' + props.uploadIdDownloadUrl + id,
status: 'done',
response: {
@@ -218,22 +238,37 @@
}
// 处理回显
const echo = (newVal) => {
const oldFileList = cloneDeep(fileList.value)
fileList.value = []
const idsToFetch = []
const urlsToFetch = []
// 字符串隔离情况
if (props.uploadResultCategory === 'interval') {
// id隔离
if (props.uploadResultType === 'id') {
newVal.split(',').forEach((id) => {
const file = buildFileObject(undefined, id)
fileList.value.push(file)
fileList.value.reverse()
const existing = oldFileList.find((f) => f.response?.data === id)
if (existing) {
fileList.value.push(existing)
} else {
const file = buildFileObject(undefined, id)
fileList.value.push(file)
idsToFetch.push(id)
}
})
}
// url隔离
if (props.uploadResultType === 'url') {
newVal.split(',').forEach((url) => {
const file = buildFileObject(url)
fileList.value.push(file)
fileList.value.reverse()
const existing = oldFileList.find((f) => f.url === url || f.response?.data === url)
if (existing) {
fileList.value.push(existing)
} else {
const file = buildFileObject(url)
fileList.value.push(file)
urlsToFetch.push(url)
}
})
}
}
@@ -258,25 +293,66 @@
// id数组
if (props.uploadResultType === 'id') {
newVal.forEach((id) => {
fileList.value.push(buildFileObject(undefined, id))
const existing = oldFileList.find((f) => f.response?.data === id)
if (existing) {
fileList.value.push(existing)
} else {
const file = buildFileObject(undefined, id)
fileList.value.push(file)
idsToFetch.push(id)
}
})
}
// url数组
if (props.uploadResultType === 'url') {
newVal.forEach((url) => {
fileList.value.push(buildFileObject(url))
const existing = oldFileList.find((f) => f.url === url || f.response?.data === url)
if (existing) {
fileList.value.push(existing)
} else {
const file = buildFileObject(url)
fileList.value.push(file)
urlsToFetch.push(url)
}
})
}
}
}
// 异步获取文件详情以修正名称
if (idsToFetch.length > 0) {
fileApi.fileGetFileListByIds(idsToFetch).then((data) => {
if (data) {
fileList.value.forEach((file) => {
const detail = data.find((d) => d.id === file.response?.data)
if (detail) {
file.name = detail.name
}
})
}
})
}
if (urlsToFetch.length > 0) {
fileApi.fileGetFileListByUrlList({ urlList: urlsToFetch }).then((data) => {
if (data) {
fileList.value.forEach((file) => {
const detail = data.find((d) => d.downloadPath === file.url || d.downloadPath === file.response?.data)
if (detail) {
file.name = detail.name
}
})
}
})
}
}
// 监听参数
watch(
() => props.value,
(newVal) => {
if (props.value && newVal) {
fileList.value = []
if (newVal) {
echo(newVal)
} else {
fileList.value = []
}
},
{ immediate: true, deep: true }
@@ -337,49 +413,49 @@
}
// 上传事件
const handleChange = (uploads) => {
let result = []
const file = uploads.file
if (file && (file.status === 'done' || file.status === 'removed') && file.response && file.response.code === 200) {
uploads.fileList.forEach((f) => {
result.push(f)
})
}
if (result.length > 0) {
if (props.uploadResultCategory === 'interval') {
const resultIntervalValue = ref('')
result.forEach((data) => {
resultIntervalValue.value =
data.response.data + (resultIntervalValue.value ? ',' + resultIntervalValue.value : '')
})
emit('update:value', resultIntervalValue)
emit('onSuccessful', resultIntervalValue)
emit('onChange', resultIntervalValue)
} else if (props.uploadResultCategory === 'array') {
if (props.completeResult) {
// 得去掉数组里面的thumbUrl,一个base64太大,无用
let newResult = cloneDeep(result)
newResult.map((e) => {
if (e.thumbUrl) {
delete e.thumbUrl
}
})
emit('update:value', newResult)
emit('onSuccessful', newResult)
emit('onChange', newResult)
} else {
const resultArrayValue = ref([])
result.forEach((data) => {
resultArrayValue.value.push(data.response.data)
})
emit('update:value', resultArrayValue)
emit('onSuccessful', resultArrayValue)
emit('onChange', resultArrayValue)
}
if (file.status === 'done' || file.status === 'removed') {
if (file.status === 'done' && file.response && file.response.code !== 200) {
message.error(file.response.message || '上传失败')
return
}
return
const result = uploads.fileList.filter((f) => f.status === 'done' && f.response && f.response.code === 200)
if (result.length > 0) {
if (props.uploadResultCategory === 'interval') {
let resultIntervalValue = ''
result.forEach((data) => {
resultIntervalValue = (resultIntervalValue ? resultIntervalValue + ',' : '') + data.response.data
})
emit('update:value', resultIntervalValue)
emit('onSuccessful', resultIntervalValue)
emit('onChange', resultIntervalValue)
} else if (props.uploadResultCategory === 'array') {
if (props.completeResult) {
// 得去掉数组里面的thumbUrl,一个base64太大,无用
let newResult = cloneDeep(result)
newResult.map((e) => {
if (e.thumbUrl) {
delete e.thumbUrl
}
})
emit('update:value', newResult)
emit('onSuccessful', newResult)
emit('onChange', newResult)
} else {
let resultArrayValue = []
result.forEach((data) => {
resultArrayValue.push(data.response.data)
})
emit('update:value', resultArrayValue)
emit('onSuccessful', resultArrayValue)
emit('onChange', resultArrayValue)
}
}
return
}
emit('update:value', undefined)
emit('onChange', undefined)
}
emit('update:value', undefined)
emit('onChange', undefined)
}
// 通过DOM获取上传的文件
const uploadFileList = () => {
+22 -6
View File
@@ -95,9 +95,9 @@
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<a-form-item label="任职信息" name="positionJson">
<a-row :gutter="10" class="bg-[var(--item-hover-bg)] p-1 mb-2.5 !mx-0">
<a-col :span="7" class="text-center truncate"> 机构 </a-col>
<a-col :span="7" class="text-center truncate"> 岗位 </a-col>
<a-col :span="7" class="text-center truncate"> 主管 </a-col>
<a-col :span="7" class="truncate"> 机构 </a-col>
<a-col :span="7" class="truncate"> 岗位 </a-col>
<a-col :span="7" class="truncate"> 主管 </a-col>
<a-col :span="3">
<a-button type="primary" @click="addDomains()" size="small">
<PlusOutlined />
@@ -107,7 +107,7 @@
</a-row>
<div :key="positionInfo" v-for="(positionInfo, index) in formData.positionJson">
<a-row :gutter="10" class="!mx-0">
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="text-center truncate">
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="truncate">
<a-form-item
:name="['positionJson', index, 'orgId']"
:rules="{ required: true, message: '请选择机构' }"
@@ -122,7 +122,7 @@
/>
</a-form-item>
</a-col>
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="text-center truncate">
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="truncate">
<a-form-item
:name="['positionJson', index, 'positionId']"
:rules="{ required: true, message: '请选择岗位' }"
@@ -137,7 +137,7 @@
/>
</a-form-item>
</a-col>
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="text-center truncate">
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="truncate">
<a-form-item :name="['positionJson', index, 'directorId']">
<xn-page-select
ref="xnChildUserPageSelectRef"
@@ -314,6 +314,16 @@
convertFormData(record).then(() => {
const allOrgIds = collectAllOrgIds()
orgTreeSelectRef.value.echo(allOrgIds)
// 子表树回显
nextTick(() => {
if (formData.value.positionJson) {
formData.value.positionJson.forEach((item, index) => {
if (childTreeSelectRefs.value[index]) {
childTreeSelectRefs.value[index].echo(allOrgIds)
}
})
}
})
})
} else if (orgId) {
orgTreeSelectRef.value.echo([orgId])
@@ -390,6 +400,12 @@
formData.value.positionJson = []
}
formData.value.positionJson.push({ orgId: undefined, positionId: undefined, directorId: undefined })
nextTick(() => {
const index = formData.value.positionJson.length - 1
if (childTreeSelectRefs.value[index]) {
childTreeSelectRefs.value[index].init()
}
})
}
const delDomains = (index) => {
+22 -7
View File
@@ -95,9 +95,9 @@
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<a-form-item label="任职信息" name="positionJson">
<a-row :gutter="10" class="bg-[var(--item-hover-bg)] p-1 mb-2.5 !mx-0">
<a-col :span="7" class="text-center truncate"> 机构 </a-col>
<a-col :span="7" class="text-center truncate"> 职位 </a-col>
<a-col :span="7" class="text-center truncate"> 主管 </a-col>
<a-col :span="7" class="truncate"> 机构 </a-col>
<a-col :span="7" class="truncate"> 职位 </a-col>
<a-col :span="7" class="truncate"> 主管 </a-col>
<a-col :span="3" class="text-center">
<a-button type="primary" @click="addDomains()" size="small">
<PlusOutlined />
@@ -107,7 +107,7 @@
</a-row>
<div :key="positionInfo" v-for="(positionInfo, index) in formData.positionJson">
<a-row :gutter="10" class="!mx-0">
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="text-center truncate">
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="truncate">
<a-form-item
:name="['positionJson', index, 'orgId']"
:rules="{ required: true, message: '请选择组织' }"
@@ -122,7 +122,7 @@
/>
</a-form-item>
</a-col>
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="text-center truncate">
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="truncate">
<a-form-item
:name="['positionJson', index, 'positionId']"
:rules="{ required: true, message: '请选择职位' }"
@@ -137,7 +137,7 @@
/>
</a-form-item>
</a-col>
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="text-center truncate">
<a-col :xs="24" :sm="24" :md="7" :lg="7" :xl="7" class="truncate">
<a-form-item :name="['positionJson', index, 'directorId']">
<xn-page-select
ref="xnChildUserPageSelectRef"
@@ -317,7 +317,16 @@
const allOrgIds = collectAllOrgIds()
// 主表树回显
orgTreeSelectRef.value.echo(allOrgIds)
// 任职信息的子行树回显(共享同一份 treeData,无需重复请求)
// 子表树回显
nextTick(() => {
if (formData.value.positionJson) {
formData.value.positionJson.forEach((item, index) => {
if (childTreeSelectRefs.value[index]) {
childTreeSelectRefs.value[index].echo(allOrgIds)
}
})
}
})
})
} else if (orgId) {
// 新增 + 预选组织
@@ -403,6 +412,12 @@
formData.value.positionJson = []
}
formData.value.positionJson.push({ orgId: undefined, positionId: undefined, directorId: undefined })
nextTick(() => {
const index = formData.value.positionJson.length - 1
if (childTreeSelectRefs.value[index]) {
childTreeSelectRefs.value[index].init()
}
})
}
// 删减行
@@ -278,6 +278,18 @@ public class DevFileController {
return CommonResult.data(devFileService.detail(devFileIdParam));
}
/**
* 根据文件id集合获取文件集合
*
* @author yubaoshan
* @date 2024/6/9 23:52
**/
@Operation(summary = "根据文件id集合获取文件集合")
@PostMapping("/dev/file/getFileListByIds")
public CommonResult<List<DevFile>> getFileListByIds(@RequestBody @Valid @NotEmpty(message = "集合不能为空") List<String> ids) {
return CommonResult.data(devFileService.getFileListByIds(ids));
}
/**
* 根据文件url集合获取文件集合
*