1.前端技术:V3 + Ant Design Vue
2.后端技术:Java
图片上传/回显:
文件上传回显:
表结构:单文件/图片上传为A表对文件C表 (A表field字段 对应 C表id字段)
如图:A表中的 vehicle_driving_license 和 driver_license 存的是C表中的id字段
表结构:多文件/图片上传为A表对文件B表 中的Biz字段,B表中的file_id字段对应C表中的id字段,(B表的 Biz 字段和 file_id 字段是一对多的存在关系)
如图:A表中的 house_type_file_id 和 house_type_balcony_close_file_id 、house_type_balcony_bisect_file_id、house_type_shearwall_file_id 存的是B表中的Biz_id字段,B表中的 field_id 字段对应 C表中的 id 字段,(B表中的Biz_id字段 与 field_id 字段是一对多的关系)
上传:(上传功能不分单个多个)java后台代码(controller):
@OperationLog@ApiOperation("上传文件")@PostMapping("/upload")public ApiResult<FileInfo> upload(@RequestParam MultipartFile file, HttpServletRequest request) {FileInfo result = null;try {String dir = getUploadDir();File upload = FileServerUtil.upload(file, dir, config.getUploadUuidName());String path = upload.getAbsolutePath().replace("\\", "/").substring(dir.length() - 1);String requestURL = StrUtil.removeSuffix(request.getRequestURL(), "/upload");requestURL = "/api/file-info/file/";String requestURL2 = "/api/file-info";/*if(requestURL.contains("10.1.140.88")){requestURL = "https://10.1.140.88/api/file";}if(requestURL.contains("djshemei.com")){requestURL = "https://10.1.140.88/api/file";}*/String originalName = file.getOriginalFilename();result = new FileInfo();result.setId(SnowFlakeGenerator.nextId());String contentType = FileServerUtil.getContentType(upload);result.setFileType(contentType);result.setFileName(StrUtil.isBlank(originalName) ? upload.getName() : originalName);result.setFilePath(path);result.setUrlPath(requestURL+result.getId());result.setUrl(requestURL2 + "/" + path);//这个用户应该是这个找登录用户User loginUser = getLoginUser();result.setCreUserId(Long.valueOf(loginUser.getUserId()));result.setCreUserName(loginUser.getUsername());result.setCreateTime(LocalDateTime.now());fileInfoService.save(result);return success(result);} catch (Exception e) {e.printStackTrace();return fail("上传失败", result).setError(e.toString());}}
前端:api代码:
/*** 上传文件*/
export async function uploadFile(file, opt) {const formData = new FormData();formData.append('file', file);const res = await request.post('/community/file-info/upload', formData, opt);if (res.data.code === 0 && res.data.data) {return res.data.data;}return Promise.reject(new Error(res.data.message));
}
在页面引入使用
<a-row><a-col :span="12"><a-form-item label="行驶证"><ele-image-uploadclass="imagestype":limit="1"v-model:value="form1.vehicleDrivingLicenseField"@upload="onUpload1"@remove="onRemove1"/></a-form-item></a-col><a-col :span="12"><a-form-item label="驾驶证"><ele-image-uploadclass="imagestype":limit="1"v-model:value="form1.driverLicenseField"@upload="onUpload2"@remove="onRemove2"/></a-form-item></a-col></a-row>
使用方法:
const onUpload1 = ({ file }) => {uploadFile(file).then((data) => {console.log(data, 'data');form1.vehicleDrivingLicenseFieldId1 = data.id;}).catch((e) => {item.status = 'exception';message.error(e.message);});};
数据结构:
// 图片const form1 = reactive({vehicleDrivingLicenseField: [],vehicleDrivingLicenseFieldId1: '',driverLicenseField: [],driverLicenseFieldId2: ''});
图片回显:java代码 (controller)
@ApiOperation("查询文件")@GetMapping("/queryFile/{id}")public ApiResult<?> getFileInfoByRoomCar (@PathVariable("id") Long id, HttpServletRequest request ) {List<RoomCar> roomCarServiceList = roomCarService.list(new QueryWrapper<RoomCar>().eq("car_id", id));if(roomCarServiceList.size() == 0){return success(new ArrayList<>());}else{List<FileInfo> fileIdList = fileInfoService.list(new QueryWrapper<FileInfo>().in("id", roomCarServiceList.stream().map(RoomCar::getVehicleDrivingLicense).collect(Collectors.toList())));if (fileIdList.size() > 0) {String requestURL = StrUtil.removeSuffix(request.getRequestURL(), "/room-car/queryFile/"+id);for (FileInfo record : fileIdList) {if (StrUtil.isNotBlank(record.getFilePath())) {record.setDownloadUrl(requestURL + "/file-info/" + record.getFilePath());record.setUrl(requestURL + "/file-info/" + record.getFilePath());}}}List<FileInfo> fileIdList1 = fileInfoService.list(new QueryWrapper<FileInfo>().in("id", roomCarServiceList.stream().map(RoomCar::getDriverLicense).collect(Collectors.toList())));if (fileIdList1.size() > 0) {String requestURL = StrUtil.removeSuffix(request.getRequestURL(), "/room-car/queryFile/"+id);for (FileInfo record : fileIdList1) {if (StrUtil.isNotBlank(record.getFilePath())) {record.setDownloadUrl(requestURL + "/file-info/" + record.getFilePath());record.setUrl(requestURL + "/file-info/" + record.getFilePath());}}}HashMap<String, List<FileInfo>> data = new HashMap<>();data.put("vehicleDrivingLicenseField", fileIdList);data.put("driverLicenseField", fileIdList1);return success(data);}}
前端api:
export async function queryItem(id) {const res = await request.get('/community/decoration-manage/queryFile/' + id);if (res.data.code === 0) {return res.data;}return Promise.reject(new Error(res.data.message));
}
页面引入使用:
watch(() => props.visible,(visible) => {if (visible) {if (props.data) {assignObject(form, {...props.data});isUpdate.value = true;showFile.value = true;changeRoomType(props.data.roomTypeId);// driverLicense// vehicleDrivingLicensequeryItem(props.data.carId).then((res) => {form1.vehicleDrivingLicenseField =res.data.vehicleDrivingLicenseField;form1.driverLicenseField = res.data.driverLicenseField;}).catch((e) => {message.error(e.message);});loadingData();} else {showFile.value = false;isUpdate.value = false;loadingData();}} else {form1.vehicleDrivingLicenseField = [];form1.driverLicenseField = [];resetFields();}});
多文件上传跟单文件上传一样的,不一样的是显示的方式:
多文件回显后端 Java代码(controller):
@ApiOperation("查询文件")@GetMapping("/queryFile/{id}")public ApiResult<?> getFileInfoByRegionalHouseTypeId(@PathVariable("id") Long id, HttpServletRequest request ) {BaseRegionalHouseType mainRec = baseRegionalHouseTypeService.getByIdRel(id);List<FileInfo> house_type_file = new ArrayList<>();List<FileInfo> house_type_balcony_close_file = new ArrayList<>();List<FileInfo> house_type_balcony_bisect_file = new ArrayList<>();List<FileInfo> house_type_shearwall_file = new ArrayList<>();Long bizId;bizId = mainRec.getHouseTypeFileId();if (bizId != null) {house_type_file = fileInfoService.getfileinfobybiz(bizId);String requestURL = StrUtil.removeSuffix(request.getRequestURL(), "/base-regional-house-type/queryFile/"+id);for (FileInfo record : house_type_file) {if (StrUtil.isNotBlank(record.getFilePath())) {record.setDownloadUrl(requestURL + "/file-info/download/" + record.getFilePath());}}}bizId = mainRec.getHouseTypeBalconyCloseFileId();if (bizId != null) {house_type_balcony_close_file = fileInfoService.getfileinfobybiz(bizId);String requestURL = StrUtil.removeSuffix(request.getRequestURL(), "/base-regional-house-type/queryFile/"+id);for (FileInfo record : house_type_balcony_close_file) {if (StrUtil.isNotBlank(record.getFilePath())) {record.setDownloadUrl(requestURL + "/file-info/download/" + record.getFilePath());}}}bizId = mainRec.getHouseTypeBalconyBisectFileId();if (bizId != null) {house_type_balcony_bisect_file = fileInfoService.getfileinfobybiz(bizId);String requestURL = StrUtil.removeSuffix(request.getRequestURL(), "/base-regional-house-type/queryFile/"+id);for (FileInfo record : house_type_balcony_bisect_file) {if (StrUtil.isNotBlank(record.getFilePath())) {record.setDownloadUrl(requestURL + "/file-info/download/" + record.getFilePath());}}}bizId = mainRec.getHouseTypeShearwallFileId();if (bizId != null) {house_type_shearwall_file = fileInfoService.getfileinfobybiz(bizId);String requestURL = StrUtil.removeSuffix(request.getRequestURL(), "/base-regional-house-type/queryFile/"+id);for (FileInfo record : house_type_shearwall_file) {if (StrUtil.isNotBlank(record.getFilePath())) {record.setDownloadUrl(requestURL + "/file-info/download/" + record.getFilePath());}}}HashMap<String, List<FileInfo>> data = new HashMap<>();data.put("house_type_file", house_type_file);data.put("house_type_balcony_close_file", house_type_balcony_close_file);data.put("house_type_balcony_bisect_file", house_type_balcony_bisect_file);data.put("house_type_shearwall_file", house_type_shearwall_file);return success(data);}
前端api:
export async function queryHouse(id) {const res = await request.get('/community/base-regional-house-type/queryFile/' + id);if (res.data.code === 0) {return res.data;}return Promise.reject(new Error(res.data.message));
}
页面使用:
<template><ele-modal:width="1200":visible="visible":confirm-loading="loading"title="文件管理":body-style="{ paddingBottom: '8px' }"@update:visible="updateVisible"@ok="save"><div class="ele-body"><a-card :bordered="false"><div class="content"><div class="loudong"><div><divstyle="font-size: 18px; font-weight: 600; margin-bottom: 25px">文件上传</div><a-row type="flex" style="margin: 20px -15px"><a-col :span="24"><a-buttontype="text"@click="typeclick(0)":class="btn == 0 ? 'btnColor' : ''"style="width: 150px">户型图</a-button></a-col></a-row><a-row type="flex" style="margin: 20px -15px"><a-col :span="24"><a-buttontype="text"@click="typeclick(1)":class="btn == 1 ? 'btnColor' : ''"style="width: 150px">封闭阳台方案</a-button></a-col></a-row><a-row type="flex" style="margin: 20px -15px"><a-col :span="24"><a-buttontype="text"@click="typeclick(2)":class="btn == 2 ? 'btnColor' : ''"style="width: 150px">封闭阳台剖面图</a-button></a-col></a-row><a-row type="flex" style="margin: 20px -15px"><a-col :span="24"><a-buttontype="text"@click="typeclick(3)":class="btn == 3 ? 'btnColor' : ''"style="width: 150px">剪力墙标识图</a-button></a-col></a-row></div></div><div class="content-right"><div class="ele-body" style="margin-top: -40px"><div class="content"><div class="content-right"><div class="content-right-header" style="margin-top: 30px"><a-upload:show-upload-list="false":customRequest="onUploadCardf"><a-button type="primary" class="ele-btn-icon"><template #icon><upload-outlined /></template><span>上传</span></a-button></a-upload></div><!-- 表格 --><ele-pro-tableborderedref="tableRef"row-key="id":columns="columns":datasource="datasource":toolkit="false":scroll="{ x: 800 }"><template #bodyCell="{ column, record, index }"><template v-if="column.key === 'action'"><a-space><a:href="record.downloadUrl"target="_blank":disabled="record.downloadUrl != null ? disabled : true">下载</a><a-divider type="vertical" /><a-popconfirmplacement="topRight"title="确定要删除此文件吗?"@confirm="remove(record, index)"><a class="ele-text-danger">删除</a></a-popconfirm></a-space></template></template></ele-pro-table></div></div></div></div></div></a-card><!-- <spbuilding-editv-model:visible="showEdit":data="current"@done="reload"/><bar-code :data="barcodedata" v-model:visible="showBarcode" /> --></div></ele-modal>
</template>
<script setup>import { ref, watch, onMounted } from 'vue';import {getfileinfobybiz,uploadFile,removeFile} from '@/api/system/file-info';import useFormData from '@/utils/use-form-data';import { Form, message } from 'ant-design-vue/es';import { messageLoading } from 'ele-admin-pro/es';import {saveHouse,queryHouse} from '@/api/baseRegionalPark/base-regional-house-type';import { CloudUploadOutlined, FileTextOutlined } from '@ant-design/icons-vue';// import FileUpload from './file-upload.vue';const emit = defineEmits(['done', 'update:visible']);const useForm = Form.useForm;const props = defineProps({data: Object,visible: Boolean});// 表单const { form, resetFields, assignFields } = useFormData({regionalHouseTypeId: '',// 户型图idhouseTypeFileId: '',// 封闭阳台方案idhouseTypeBalconyCloseFileId: '',// 封闭阳台剖面图idhouseTypeBalconyBisectFileId: '',// 剪力墙标识图idhouseTypeShearwallFileId: '',house_type_file: [],house_type_balcony_close_file: [],house_type_balcony_bisect_file: [],house_type_shearwall_file: []});// 按钮颜色const btn = ref(0);// 确定数据const datas = ref({regionalHouseTypeId: '',house_type_file: [],house_type_balcony_close_file: [],house_type_balcony_bisect_file: [],house_type_shearwall_file: [],downloadUrl:"",// 户型图idhouseTypeFileId: '',// 封闭阳台方案idhouseTypeBalconyCloseFileId: '',// 封闭阳台剖面图idhouseTypeBalconyBisectFileId: '',// 剪力墙标识图idhouseTypeShearwallFileId: '',});const typeclick = (type) => {switch (type) {case 0:btn.value = 0;datasource.value = datas.value.house_type_file; break;case 1:btn.value = 1;datasource.value = datas.value.house_type_balcony_close_file; break;case 2:btn.value = 2;datasource.value = datas.value.house_type_balcony_bisect_file; break;case 3:btn.value = 3;datasource.value = datas.value.house_type_shearwall_file; break;} };const findPicIds = ref([]);// 表格实例const tableRef = ref(null);// 导入请求状态const loading = ref(false);const isAdmin = ref(false);// 保存按钮const save = () => {saveHouse(datas.value).then((res) => {if (res.code == 0) {message.success('保存成功');emit('done');emit('update:visible', false);} else {message.error(res.msg);}});};// 表格列配置const columns = ref([{title: '序号',key: 'index',width: 48,align: 'center',fixed: 'left',hideInSetting: true,customRender: ({ index }) => index + (tableRef.value?.tableIndex ?? 0)},{title: '文件名',dataIndex: 'fileName'},{title: '文件类型',dataIndex: 'fileType'},{title: '创建人',dataIndex: 'creUserName'},{title: '创建时间',dataIndex: 'createTime'},{title: '操作',key: 'action',width: 160,align: 'center',hideInSetting: true}]);//上传文件const onUploadCardf = (d) => {uploadFile(d.file, {onUploadProgress: (e) => {if (e.lengthComputable) {d.progress = (e.loaded / e.total) * 100;}}}).then((data) => {d.status = 'done';if (btn.value == 0) { datas.value.house_type_file.push(data);} else if (btn.value == 1) {datas.value.house_type_balcony_close_file.push(data);} else if (btn.value == 2) {datas.value.house_type_balcony_bisect_file.push(data);} else if (btn.value == 3) {datas.value.house_type_shearwall_file.push(data);}message.success('上传成功');}).catch((e) => {message.error(e.message);});};// /* 删除单个 */const remove = (row, index) => {const hide = message.loading('请求中..', 0);removeFile(row.id).then((msg) => {var arr = [];if(btn.value ==0 ){arr = datas.value.house_type_file.filter((d) => d.id != row.id);datas.value.house_type_file = arr;}if(btn.value ==1 ){arr = datas.value.house_type_balcony_close_file.filter((d) => d.id != row.id);datas.value.house_type_balcony_close_file = arr;}if(btn.value ==2 ){arr = datas.value.house_type_balcony_bisect_file.filter((d) => d.id != row.id);datas.value.house_type_balcony_bisect_file = arr;}if(btn.value ==3 ){arr = datas.value.house_type_shearwall_file.filter((d) => d.id != row.id);datas.value.house_type_shearwall_file = arr;}typeclick(btn.value);hide();message.success(msg);}).catch((e) => {hide();message.error(e.message);});};// // 表格数据源const datasource = ref([]);/* 更新visible */const updateVisible = (value) => {emit('update:visible', value);};watch(() => props.visible,(visible) => {if (visible) {if (!props.data) {alert('数据为空,请确保传递正确数据');return;}console.log(props.data);datas.value.regionalHouseTypeId = props.data.regionalHouseTypeId; queryHouse(datas.value.regionalHouseTypeId).then((res) => {datas.value.house_type_file = res.data.house_type_file;datas.value.house_type_balcony_close_file = res.data.house_type_balcony_close_file;datas.value.house_type_balcony_bisect_file = res.data.house_type_balcony_bisect_file;datas.value.house_type_shearwall_file = res.data.house_type_shearwall_file;// 默认选中第一个typeclick(0);}); }});
</script>
<style lang="less" scoped>// .ele-body {// height: 100%;// }.btnColor {background-color: #f4fbf8;color: #1677ff;}.content {display: flex;.loudong {width: 280px;margin-right: 15px;padding: 15px;// height: 80vh;background: #fff;overflow: auto;box-sizing: border-box;}.search {width: 100%;padding: 20px 10px 0px 20px;background: #f6f5f5;margin-bottom: 5px;}.content-right {flex: 1;}}
</style>
还有一个小细节,java存储文件id的字段一定不要忽略修改的时候传来的null
用注解:
@TableField(updateStrategy = FieldStrategy.IGNORED)
就ok啦,暂时先做个笔记,后面有空再慢慢写注解