【功能】新增文件批量上传组件以及实现+增加业务类型与业务id

master
zengchenxi 9 months ago
parent 2904b6c289
commit 297c3fb486

@ -38,4 +38,15 @@ public interface FileApi {
*/ */
String createFile(String name, String path, byte[] content); String createFile(String name, String path, byte[] content);
/**
* 访
* @param name
* @param path
* @param content
* @param businessType
* @param businessId
* @return
*/
String createFile(String name, String path, byte[] content, String businessType, Long businessId);
} }

@ -20,7 +20,12 @@ public class FileApiImpl implements FileApi {
@Override @Override
public String createFile(String name, String path, byte[] content) { public String createFile(String name, String path, byte[] content) {
return fileService.createFile(name, path, content); return fileService.createFile(name, path, content, null, null);
}
@Override
public String createFile(String name, String path, byte[] content, String businessType, Long businessId) {
return fileService.createFile(name, path, content, businessType, businessId);
} }
} }

@ -10,6 +10,7 @@ import com.chanko.yunxi.mes.framework.common.util.servlet.ServletUtils;
import com.chanko.yunxi.mes.framework.operatelog.core.annotations.OperateLog; import com.chanko.yunxi.mes.framework.operatelog.core.annotations.OperateLog;
import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FilePageReqVO; import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FileRespVO; import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FileRespVO;
import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FileUploadBatchReqVO;
import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FileUploadReqVO; import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FileUploadReqVO;
import com.chanko.yunxi.mes.module.infra.dal.dataobject.file.FileDO; import com.chanko.yunxi.mes.module.infra.dal.dataobject.file.FileDO;
import com.chanko.yunxi.mes.module.infra.service.file.FileService; import com.chanko.yunxi.mes.module.infra.service.file.FileService;
@ -47,7 +48,18 @@ public class FileController {
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception { public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile(); MultipartFile file = uploadReqVO.getFile();
String path = uploadReqVO.getPath(); String path = uploadReqVO.getPath();
return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream()))); return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream()), uploadReqVO.getBusinessType(), uploadReqVO.getBusinessId()));
}
@PostMapping("/uploadBatch")
@Operation(summary = "上传文件(批量)")
@OperateLog(logArgs = false) // 上传文件,没有记录操作日志的必要
public CommonResult<Void> uploadFileBatch(FileUploadBatchReqVO uploadReqVO) throws Exception {
MultipartFile[] files = uploadReqVO.getFiles();
for (MultipartFile file : files) {
fileService.createFile(file.getOriginalFilename(), null, IoUtil.readBytes(file.getInputStream()), uploadReqVO.getBusinessType(), uploadReqVO.getBusinessId());
}
return success(null);
} }
@DeleteMapping("/delete") @DeleteMapping("/delete")

@ -17,12 +17,30 @@ import static com.chanko.yunxi.mes.framework.common.util.date.DateUtils.FORMAT_Y
@ToString(callSuper = true) @ToString(callSuper = true)
public class FilePageReqVO extends PageParam { public class FilePageReqVO extends PageParam {
@Schema(description = "文件路径,模糊匹配", example = "mes") @Schema(description = "配置编号", example = "6911")
private Long configId;
@Schema(description = "业务类型 用于业务关联", example = "1")
private String businessType;
@Schema(description = "业务id", example = "18052")
private Long businessId;
@Schema(description = "文件名", example = "芋艿")
private String name;
@Schema(description = "文件路径")
private String path; private String path;
@Schema(description = "文件类型,模糊匹配", example = "jpg") @Schema(description = "文件 URL", example = "https://www.iocoder.cn")
private String url;
@Schema(description = "文件类型", example = "1")
private String type; private String type;
@Schema(description = "文件大小")
private Integer size;
@Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") @Schema(description = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime; private LocalDateTime[] createTime;

@ -15,6 +15,12 @@ public class FileRespVO {
@Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11") @Schema(description = "配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "11")
private Long configId; private Long configId;
@Schema(description = "业务类型 用于业务关联", example = "1")
private String businessType;
@Schema(description = "业务id", example = "18052")
private Long businessId;
@Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "mes.jpg") @Schema(description = "文件路径", requiredMode = Schema.RequiredMode.REQUIRED, example = "mes.jpg")
private String path; private String path;

@ -0,0 +1,25 @@
package com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 上传文件 Request VO")
@Data
public class FileUploadBatchReqVO {
@Schema(description = "上传文件", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "上传文件不能为空")
@NotEmpty(message = "上传文件不能为空")
private MultipartFile[] files;
@Schema(description = "文件业务类型")
private String businessType;
@Schema(description = "文件业务id")
private Long businessId;
}

@ -17,4 +17,10 @@ public class FileUploadReqVO {
@Schema(description = "文件附件", example = "mesyuanma.png") @Schema(description = "文件附件", example = "mesyuanma.png")
private String path; private String path;
@Schema(description = "文件业务类型")
private String businessType;
@Schema(description = "文件业务id")
private Long businessId;
} }

@ -32,7 +32,7 @@ public class AppFileController {
public CommonResult<String> uploadFile(AppFileUploadReqVO uploadReqVO) throws Exception { public CommonResult<String> uploadFile(AppFileUploadReqVO uploadReqVO) throws Exception {
MultipartFile file = uploadReqVO.getFile(); MultipartFile file = uploadReqVO.getFile();
String path = uploadReqVO.getPath(); String path = uploadReqVO.getPath();
return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream()))); return success(fileService.createFile(file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream()), uploadReqVO.getBusinessType(), uploadReqVO.getBusinessId()));
} }
} }

@ -14,6 +14,12 @@ public class AppFileUploadReqVO {
@NotNull(message = "文件附件不能为空") @NotNull(message = "文件附件不能为空")
private MultipartFile file; private MultipartFile file;
@Schema(description = "文件业务类型")
private String businessType;
@Schema(description = "文件业务id")
private Long businessId;
@Schema(description = "文件附件", example = "mesyuanma.png") @Schema(description = "文件附件", example = "mesyuanma.png")
private String path; private String path;

@ -31,6 +31,14 @@ public class FileDO extends BaseDO {
* {@link FileConfigDO#getId()} * {@link FileConfigDO#getId()}
*/ */
private Long configId; private Long configId;
/**
*
*/
private String businessType;
/**
* id
*/
private Long businessId;
/** /**
* *
*/ */

@ -17,8 +17,14 @@ public interface FileMapper extends BaseMapperX<FileDO> {
default PageResult<FileDO> selectPage(FilePageReqVO reqVO) { default PageResult<FileDO> selectPage(FilePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<FileDO>() return selectPage(reqVO, new LambdaQueryWrapperX<FileDO>()
.likeIfPresent(FileDO::getPath, reqVO.getPath()) .eqIfPresent(FileDO::getConfigId, reqVO.getConfigId())
.likeIfPresent(FileDO::getType, reqVO.getType()) .eqIfPresent(FileDO::getBusinessType, reqVO.getBusinessType())
.eqIfPresent(FileDO::getBusinessId, reqVO.getBusinessId())
.likeIfPresent(FileDO::getName, reqVO.getName())
.eqIfPresent(FileDO::getPath, reqVO.getPath())
.eqIfPresent(FileDO::getUrl, reqVO.getUrl())
.eqIfPresent(FileDO::getType, reqVO.getType())
.eqIfPresent(FileDO::getSize, reqVO.getSize())
.betweenIfPresent(FileDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(FileDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(FileDO::getId)); .orderByDesc(FileDO::getId));
} }

@ -1,7 +1,7 @@
package com.chanko.yunxi.mes.module.infra.service.file; package com.chanko.yunxi.mes.module.infra.service.file;
import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import com.chanko.yunxi.mes.framework.common.pojo.PageResult; import com.chanko.yunxi.mes.framework.common.pojo.PageResult;
import com.chanko.yunxi.mes.module.infra.controller.admin.file.vo.file.FilePageReqVO;
import com.chanko.yunxi.mes.module.infra.dal.dataobject.file.FileDO; import com.chanko.yunxi.mes.module.infra.dal.dataobject.file.FileDO;
/** /**
@ -22,12 +22,14 @@ public interface FileService {
/** /**
* 访 * 访
* *
* @param name * @param name
* @param path * @param path
* @param content * @param content
* @param businessType
* @param businessId
* @return * @return
*/ */
String createFile(String name, String path, byte[] content); String createFile(String name, String path, byte[] content, String businessType, Long businessId);
/** /**
* *

@ -38,7 +38,7 @@ public class FileServiceImpl implements FileService {
@Override @Override
@SneakyThrows @SneakyThrows
public String createFile(String name, String path, byte[] content) { public String createFile(String name, String path, byte[] content, String businessType, Long businessId) {
// 计算默认的 path 名 // 计算默认的 path 名
String type = FileTypeUtils.getMineType(content, name); String type = FileTypeUtils.getMineType(content, name);
if (StrUtil.isEmpty(path)) { if (StrUtil.isEmpty(path)) {
@ -56,6 +56,8 @@ public class FileServiceImpl implements FileService {
// 保存到数据库 // 保存到数据库
FileDO file = new FileDO(); FileDO file = new FileDO();
file.setBusinessId(businessId);
file.setBusinessType(businessType);
file.setConfigId(client.getId()); file.setConfigId(client.getId());
file.setName(name); file.setName(name);
file.setPath(path); file.setPath(path);

@ -9,6 +9,9 @@ VITE_BASE_URL='http://192.168.0.137:8080'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://192.168.0.137:8080/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://192.168.0.137:8080/admin-api/infra/file/upload'
# 上传路径
VITE_UPLOAD_BATCH_URL='http://192.168.0.137:8080/admin-api/infra/file/uploadBatch'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH=/dev-api VITE_API_BASEPATH=/dev-api

@ -9,6 +9,9 @@ VITE_BASE_URL='http://222.71.165.187:9010'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://222.71.165.187:9010/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://222.71.165.187:9010/admin-api/infra/file/upload'
# 上传路径
VITE_UPLOAD_BATCH_URL='http://222.71.165.187:9010/admin-api/infra/file/uploadBatch'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH=/dev-api VITE_API_BASEPATH=/dev-api

@ -0,0 +1,149 @@
<template>
<div class="upload-file">
<el-upload
ref="uploadRef"
:multiple="props.limit > 1"
name="files"
v-model="valueRef"
v-model:file-list="fileList"
:show-file-list="true"
:auto-upload="autoUpload"
:action="updateUrl"
:headers="uploadHeaders"
:data="{'businessType': businessType, 'businessId': businessId}"
:limit="props.limit"
:drag="drag"
:before-upload="beforeUpload"
:on-exceed="handleExceed"
:on-success="handleFileSuccess"
:on-error="excelUploadError"
:on-remove="handleRemove"
:on-preview="handlePreview"
class="upload-file-uploader"
>
<el-button type="primary"><Icon icon="ep:upload-filled" />选取文件</el-button>
<template v-if="isShowTip" #tip>
<div style="font-size: 8px">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</div>
<!-- <div style="font-size: 8px">
格式为 <b style="color: #f56c6c">{{ fileType.join('/') }}</b> 的文件
</div>-->
</template>
</el-upload>
</div>
</template>
<script lang="ts" setup>
import { propTypes } from '@/utils/propTypes'
import { getAccessToken, getTenantId } from '@/utils/auth'
import type { UploadInstance, UploadUserFile, UploadProps, UploadRawFile } from 'element-plus'
defineOptions({ name: 'UploadFileBatch' })
const message = useMessage() //
const emit = defineEmits(['update:modelValue'])
const props = defineProps({
businessType: propTypes.string,
businessId: propTypes.number,
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
title: propTypes.string.def('文件上传'),
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_BATCH_URL),
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // , ['png', 'jpg', 'jpeg']
fileSize: propTypes.number.def(20), // (MB)
limit: propTypes.number.def(5), //
autoUpload: propTypes.bool.def(false), //
drag: propTypes.bool.def(false), //
isShowTip: propTypes.bool.def(true) //
})
// ========== ==========
const valueRef = ref(props.modelValue)
const uploadRef = ref<UploadInstance>()
const uploadList = ref<UploadUserFile[]>([])
const fileList = ref<UploadUserFile[]>([])
const uploadNumber = ref<number>(0)
const uploadHeaders = ref({
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
})
//
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
if (fileList.value.length >= props.limit) {
message.error(`上传文件数量不能超过${props.limit}个!`)
return false
}
//
/*let fileExtension = ''
if (file.name.lastIndexOf('.') > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
}
const isImg = props.fileType.some((type: string) => {
if (file.type.indexOf(type) > -1) return true
return !!(fileExtension && fileExtension.indexOf(type) > -1)
})*/
const isLimit = file.size < props.fileSize * 1024 * 1024
/*if (!isImg) {
message.error(`文件格式不正确, 请上传${props.fileType.join('/')}格式!`)
return false
}*/
if (!isLimit) {
message.error(`上传文件大小不能超过${props.fileSize}MB!`)
return false
}
message.success('正在上传文件,请稍候...')
uploadNumber.value++
}
//
const handleFileSuccess: UploadProps['onSuccess'] = (res: any): void => {
message.success('上传成功')
}
//
const handleExceed: UploadProps['onExceed'] = (): void => {
message.error(`上传文件数量不能超过${props.limit}个!`)
}
//
const excelUploadError: UploadProps['onError'] = (): void => {
message.error('上传文件失败,请您重新上传!')
}
//
const handleRemove = (file) => {
const findex = fileList.value.map((f) => f.name).indexOf(file.name)
if (findex > -1) {
fileList.value.splice(findex, 1)
}
}
const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
console.log(uploadFile)
}
const submitUpload = async () => {
uploadRef.value!.submit();
}
defineExpose({submitUpload})
</script>
<style scoped lang="scss">
.upload-file-uploader {
margin-bottom: 5px;
}
:deep(.upload-file-list .el-upload-list__item) {
position: relative;
margin-bottom: 10px;
line-height: 2;
border: 1px solid #e4e7ed;
}
:deep(.el-upload-list__item-file-name) {
max-width: 250px;
}
:deep(.upload-file-list .ele-upload-list__item-content) {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
}
:deep(.ele-upload-list__item-content-action .el-link) {
margin-right: 10px;
}
</style>
Loading…
Cancel
Save