feat: 附件上传
This commit is contained in:
@@ -110,4 +110,6 @@ public interface DevFileApi {
|
|||||||
* @date 2024/7/26 16:10
|
* @date 2024/7/26 16:10
|
||||||
*/
|
*/
|
||||||
JSONObject getFileInfoById(String id);
|
JSONObject getFileInfoById(String id);
|
||||||
|
|
||||||
|
JSONObject getFileInfoByUrl(String fileAddress);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ package org.nl.dev.modular.file.provider;
|
|||||||
|
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.nl.dev.modular.file.entity.DevFile;
|
||||||
import org.nl.dev.modular.file.enums.DevFileEngineTypeEnum;
|
import org.nl.dev.modular.file.enums.DevFileEngineTypeEnum;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
@@ -81,4 +83,12 @@ public class DevFileApiProvider implements DevFileApi {
|
|||||||
.map(JSONUtil::parseObj)
|
.map(JSONUtil::parseObj)
|
||||||
.orElse(new JSONObject());
|
.orElse(new JSONObject());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JSONObject getFileInfoByUrl(String fileAddress) {
|
||||||
|
return Optional.ofNullable(devFileService.getOne(new LambdaQueryWrapper<DevFile>()
|
||||||
|
.eq(DevFile::getDownloadPath, fileAddress)))
|
||||||
|
.map(JSONUtil::parseObj)
|
||||||
|
.orElse(new JSONObject());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@
|
|||||||
<groupId>org.nl</groupId>
|
<groupId>org.nl</groupId>
|
||||||
<artifactId>nl-plugin-sys-api</artifactId>
|
<artifactId>nl-plugin-sys-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.nl</groupId>
|
||||||
|
<artifactId>nl-plugin-dev-api</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
import org.nl.common.pojo.SelectListItem;
|
import org.nl.common.pojo.SelectListItem;
|
||||||
|
import org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo;
|
||||||
import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
|
import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
|
||||||
import org.nl.pmm.modular.project.entity.vo.ProjectVo;
|
import org.nl.pmm.modular.project.entity.vo.ProjectVo;
|
||||||
import org.nl.pmm.modular.project.param.*;
|
import org.nl.pmm.modular.project.param.*;
|
||||||
@@ -130,12 +131,24 @@ public class ProjectController {
|
|||||||
*/
|
*/
|
||||||
@Operation(summary = "获取项目需求信息详情")
|
@Operation(summary = "获取项目需求信息详情")
|
||||||
// @SaCheckPermission("/pmm/project/request-detail")
|
// @SaCheckPermission("/pmm/project/request-detail")
|
||||||
@SaIgnore
|
|
||||||
@GetMapping("/pmm/project/requestDetail")
|
@GetMapping("/pmm/project/requestDetail")
|
||||||
public CommonResult<List<ProjectRequestVo>> requestDetail(@Valid ProjectIdParam projectIdParam) {
|
public CommonResult<List<ProjectRequestVo>> requestDetail(@Valid ProjectIdParam projectIdParam) {
|
||||||
return CommonResult.data(projectService.requestDetail(projectIdParam));
|
return CommonResult.data(projectService.requestDetail(projectIdParam));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目附件
|
||||||
|
*
|
||||||
|
* @author liyongde
|
||||||
|
* @date 2025/11/11 20:13
|
||||||
|
*/
|
||||||
|
@Operation(summary = "获取项目需求信息详情")
|
||||||
|
// @SaCheckPermission("/pmm/project/request-detail")
|
||||||
|
@GetMapping("/pmm/project/getAttachment")
|
||||||
|
public CommonResult<List<ProjectAttachmentVo>> getAttachment(@Valid ProjectIdParam projectIdParam) {
|
||||||
|
return CommonResult.data(projectService.getAttachment(projectIdParam));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取xx组用户
|
* 获取xx组用户
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package org.nl.pmm.modular.project.entity.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: lyd
|
||||||
|
* @Date: 2025/11/19
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ProjectAttachmentVo {
|
||||||
|
private String id;
|
||||||
|
private String name;
|
||||||
|
private String suffix;
|
||||||
|
private String downloadPath;
|
||||||
|
private String thumbnail;
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
import org.nl.common.pojo.SelectListItem;
|
import org.nl.common.pojo.SelectListItem;
|
||||||
import org.nl.pmm.modular.project.entity.Project;
|
import org.nl.pmm.modular.project.entity.Project;
|
||||||
|
import org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo;
|
||||||
import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
|
import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
|
||||||
import org.nl.pmm.modular.project.entity.vo.ProjectVo;
|
import org.nl.pmm.modular.project.entity.vo.ProjectVo;
|
||||||
import org.nl.pmm.modular.project.param.ProjectAddParam;
|
import org.nl.pmm.modular.project.param.ProjectAddParam;
|
||||||
@@ -92,4 +93,11 @@ public interface ProjectService extends IService<Project> {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<ProjectRequestVo> requestDetail(ProjectIdParam projectIdParam);
|
List<ProjectRequestVo> requestDetail(ProjectIdParam projectIdParam);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目附件
|
||||||
|
* @param projectIdParam
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
List<ProjectAttachmentVo> getAttachment(ProjectIdParam projectIdParam);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.nl.common.pojo.SelectListItem;
|
import org.nl.common.pojo.SelectListItem;
|
||||||
import org.nl.common.util.CommonTimeFormatUtil;
|
import org.nl.common.util.CommonTimeFormatUtil;
|
||||||
|
import org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo;
|
||||||
import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
|
import org.nl.pmm.modular.project.entity.vo.ProjectRequestVo;
|
||||||
import org.nl.pmm.modular.project.entity.vo.ProjectVo;
|
import org.nl.pmm.modular.project.entity.vo.ProjectVo;
|
||||||
|
import org.nl.pmm.modular.projectfile.service.ProjectFileService;
|
||||||
import org.nl.pmm.modular.projectstage.entity.ProjectStage;
|
import org.nl.pmm.modular.projectstage.entity.ProjectStage;
|
||||||
import org.nl.pmm.modular.projectstage.service.ProjectStageService;
|
import org.nl.pmm.modular.projectstage.service.ProjectStageService;
|
||||||
import org.nl.pmm.modular.stagedetail.entity.StageDetail;
|
import org.nl.pmm.modular.stagedetail.entity.StageDetail;
|
||||||
@@ -63,6 +65,8 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
|
|||||||
private ProjectStageService projectStageService;
|
private ProjectStageService projectStageService;
|
||||||
@Resource
|
@Resource
|
||||||
private StageDetailService stageDetailService;
|
private StageDetailService stageDetailService;
|
||||||
|
@Resource
|
||||||
|
private ProjectFileService projectFileService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<ProjectVo> page(ProjectPageParam projectPageParam) {
|
public Page<ProjectVo> page(ProjectPageParam projectPageParam) {
|
||||||
@@ -176,4 +180,11 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProjectAttachmentVo> getAttachment(ProjectIdParam projectIdParam) {
|
||||||
|
// 获取项目附件
|
||||||
|
List<ProjectAttachmentVo> files = projectFileService.getFileByProjectId(projectIdParam.getProjectId());
|
||||||
|
return files;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,11 @@
|
|||||||
package org.nl.pmm.modular.projectfile.controller;
|
package org.nl.pmm.modular.projectfile.controller;
|
||||||
|
|
||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import cn.dev33.satoken.annotation.SaIgnore;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import org.nl.pmm.modular.projectfile.param.*;
|
||||||
import org.springframework.validation.annotation.Validated;
|
import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -24,10 +26,6 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
import org.nl.common.annotation.CommonLog;
|
import org.nl.common.annotation.CommonLog;
|
||||||
import org.nl.common.pojo.CommonResult;
|
import org.nl.common.pojo.CommonResult;
|
||||||
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFileAddParam;
|
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFileEditParam;
|
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFileIdParam;
|
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFilePageParam;
|
|
||||||
import org.nl.pmm.modular.projectfile.service.ProjectFileService;
|
import org.nl.pmm.modular.projectfile.service.ProjectFileService;
|
||||||
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -107,6 +105,21 @@ public class ProjectFileController {
|
|||||||
projectFileService.delete(projectFileIdParamList);
|
projectFileService.delete(projectFileIdParamList);
|
||||||
return CommonResult.ok();
|
return CommonResult.ok();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 删除项目附件表
|
||||||
|
*
|
||||||
|
* @author liyongde
|
||||||
|
* @date 2025/11/17 20:24
|
||||||
|
*/
|
||||||
|
@Operation(summary = "保存项目附件表")
|
||||||
|
@CommonLog("保存项目附件表")
|
||||||
|
@SaIgnore
|
||||||
|
// @SaCheckPermission("/pmm/projectfile/bindProjectFile")
|
||||||
|
@PostMapping("/pmm/projectfile/bindProjectFile")
|
||||||
|
public CommonResult<String> bindProjectFile(@RequestBody ProjectFileAddParam projectFileIdParam) {
|
||||||
|
projectFileService.bindProjectFile(projectFileIdParam);
|
||||||
|
return CommonResult.ok();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取项目附件表详情
|
* 获取项目附件表详情
|
||||||
|
|||||||
@@ -13,8 +13,11 @@
|
|||||||
package org.nl.pmm.modular.projectfile.mapper;
|
package org.nl.pmm.modular.projectfile.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo;
|
||||||
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 项目附件表Mapper接口
|
* 项目附件表Mapper接口
|
||||||
*
|
*
|
||||||
@@ -22,4 +25,5 @@ import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
|||||||
* @date 2025/11/17 20:24
|
* @date 2025/11/17 20:24
|
||||||
**/
|
**/
|
||||||
public interface ProjectFileMapper extends BaseMapper<ProjectFile> {
|
public interface ProjectFileMapper extends BaseMapper<ProjectFile> {
|
||||||
|
List<ProjectAttachmentVo> getFileByProjectId(String projectId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,14 @@
|
|||||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="org.nl.pmm.modular.projectfile.mapper.ProjectFileMapper">
|
<mapper namespace="org.nl.pmm.modular.projectfile.mapper.ProjectFileMapper">
|
||||||
|
|
||||||
</mapper>
|
<select id="getFileByProjectId" resultType="org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo">
|
||||||
|
SELECT df.ID,
|
||||||
|
df.`NAME`,
|
||||||
|
df.SUFFIX,
|
||||||
|
df.DOWNLOAD_PATH
|
||||||
|
FROM dev_file df
|
||||||
|
WHERE df.ID IN (SELECT pf.file_id
|
||||||
|
FROM `pmm_project_file` pf
|
||||||
|
WHERE pf.project_id = #{projectId})
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package org.nl.pmm.modular.projectfile.param;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: lyd
|
||||||
|
* @Date: 2025/11/19
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ProjectFileBindParam {
|
||||||
|
/** 项目id */
|
||||||
|
@Schema(description = "项目id")
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
/** 文件地址 */
|
||||||
|
@Schema(description = "文件地址")
|
||||||
|
private String fileUrl;
|
||||||
|
}
|
||||||
@@ -14,11 +14,9 @@ package org.nl.pmm.modular.projectfile.service;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo;
|
||||||
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFileAddParam;
|
import org.nl.pmm.modular.projectfile.param.*;
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFileEditParam;
|
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFileIdParam;
|
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFilePageParam;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -77,4 +75,8 @@ public interface ProjectFileService extends IService<ProjectFile> {
|
|||||||
* @date 2025/11/17 20:24
|
* @date 2025/11/17 20:24
|
||||||
**/
|
**/
|
||||||
ProjectFile queryEntity(String id);
|
ProjectFile queryEntity(String id);
|
||||||
|
|
||||||
|
List<ProjectAttachmentVo> getFileByProjectId(String projectId);
|
||||||
|
|
||||||
|
void bindProjectFile(ProjectFileAddParam param);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,16 @@ import cn.hutool.core.bean.BeanUtil;
|
|||||||
import cn.hutool.core.collection.CollStreamUtil;
|
import cn.hutool.core.collection.CollStreamUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
import org.springframework.stereotype.Service;
|
import jakarta.annotation.Resource;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.nl.common.enums.CommonSortOrderEnum;
|
import org.nl.common.enums.CommonSortOrderEnum;
|
||||||
import org.nl.common.exception.CommonException;
|
import org.nl.common.exception.CommonException;
|
||||||
import org.nl.common.page.CommonPageRequest;
|
import org.nl.common.page.CommonPageRequest;
|
||||||
|
import org.nl.dev.api.DevFileApi;
|
||||||
|
import org.nl.pmm.modular.project.entity.vo.ProjectAttachmentVo;
|
||||||
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
import org.nl.pmm.modular.projectfile.entity.ProjectFile;
|
||||||
import org.nl.pmm.modular.projectfile.mapper.ProjectFileMapper;
|
import org.nl.pmm.modular.projectfile.mapper.ProjectFileMapper;
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFileAddParam;
|
import org.nl.pmm.modular.projectfile.param.ProjectFileAddParam;
|
||||||
@@ -31,6 +33,8 @@ import org.nl.pmm.modular.projectfile.param.ProjectFileEditParam;
|
|||||||
import org.nl.pmm.modular.projectfile.param.ProjectFileIdParam;
|
import org.nl.pmm.modular.projectfile.param.ProjectFileIdParam;
|
||||||
import org.nl.pmm.modular.projectfile.param.ProjectFilePageParam;
|
import org.nl.pmm.modular.projectfile.param.ProjectFilePageParam;
|
||||||
import org.nl.pmm.modular.projectfile.service.ProjectFileService;
|
import org.nl.pmm.modular.projectfile.service.ProjectFileService;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -43,6 +47,9 @@ import java.util.List;
|
|||||||
@Service
|
@Service
|
||||||
public class ProjectFileServiceImpl extends ServiceImpl<ProjectFileMapper, ProjectFile> implements ProjectFileService {
|
public class ProjectFileServiceImpl extends ServiceImpl<ProjectFileMapper, ProjectFile> implements ProjectFileService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DevFileApi devFileApi;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<ProjectFile> page(ProjectFilePageParam projectFilePageParam) {
|
public Page<ProjectFile> page(ProjectFilePageParam projectFilePageParam) {
|
||||||
QueryWrapper<ProjectFile> queryWrapper = new QueryWrapper<ProjectFile>().checkSqlInjection();
|
QueryWrapper<ProjectFile> queryWrapper = new QueryWrapper<ProjectFile>().checkSqlInjection();
|
||||||
@@ -91,4 +98,17 @@ public class ProjectFileServiceImpl extends ServiceImpl<ProjectFileMapper, Proje
|
|||||||
}
|
}
|
||||||
return projectFile;
|
return projectFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ProjectAttachmentVo> getFileByProjectId(String projectId) {
|
||||||
|
return this.baseMapper.getFileByProjectId(projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindProjectFile(ProjectFileAddParam param) {
|
||||||
|
JSONObject file = devFileApi.getFileInfoByUrl(param.getFileAddress());
|
||||||
|
param.setFileId(file.getStr("id"));
|
||||||
|
param.setFileAddress(file.getStr("downloadPath"));
|
||||||
|
add(param);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,10 @@ export default {
|
|||||||
requestDetail(data) {
|
requestDetail(data) {
|
||||||
return request('requestDetail', data, 'get')
|
return request('requestDetail', data, 'get')
|
||||||
},
|
},
|
||||||
|
// 获取项目附件信息
|
||||||
|
getAttachment(data) {
|
||||||
|
return request('getAttachment', data, 'get')
|
||||||
|
},
|
||||||
// 获取用户信息详情
|
// 获取用户信息详情
|
||||||
devUsers(data) {
|
devUsers(data) {
|
||||||
return request('dev-all-users', data, 'get')
|
return request('dev-all-users', data, 'get')
|
||||||
|
|||||||
@@ -24,5 +24,9 @@ export default {
|
|||||||
// 获取项目附件表详情
|
// 获取项目附件表详情
|
||||||
projectFileDetail(data) {
|
projectFileDetail(data) {
|
||||||
return request('detail', data, 'get')
|
return request('detail', data, 'get')
|
||||||
|
},
|
||||||
|
// 获取项目附件表详情
|
||||||
|
bindProjectFile(data) {
|
||||||
|
return request('bindProjectFile', data, 'post')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-card :bordered="false">
|
<div class="description-wrapper">
|
||||||
<a-spin :spinning="loading">
|
<div v-if="!previewVisible">
|
||||||
|
<a-card :bordered="false">
|
||||||
|
<a-spin :spinning="loading">
|
||||||
<!-- 项目名称标题 -->
|
<!-- 项目名称标题 -->
|
||||||
<div class="project-title">
|
<div class="project-title">
|
||||||
<h2>{{ parsedQuery?.projectName || '项目名称' }}</h2>
|
<h2>{{ parsedQuery?.projectName || '项目名称' }}</h2>
|
||||||
@@ -140,17 +142,56 @@
|
|||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
<a-empty v-else description="暂无数据" />
|
<a-empty v-else description="暂无数据" />
|
||||||
</a-spin>
|
|
||||||
</a-card>
|
<!-- 附件区域 -->
|
||||||
<stage-add-dialog ref="formRef" @successful="fetchDetail"/>
|
<div class="attachments-section">
|
||||||
|
<div class="attachments-header">
|
||||||
|
<div class="attachments-title">附件</div>
|
||||||
|
<a-button type="primary" @click="uploadFormRef.openUpload(projectInfo.projectId)">上传附件</a-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="attachmentList.length" class="attachment-list">
|
||||||
|
<div
|
||||||
|
v-for="file in attachmentList"
|
||||||
|
:key="file.id"
|
||||||
|
class="attachment-item"
|
||||||
|
@click="handlePreview(file)"
|
||||||
|
>
|
||||||
|
<div class="attachment-thumb">
|
||||||
|
<img :src="getAttachmentThumbnail(file)" :alt="file.name" />
|
||||||
|
</div>
|
||||||
|
<div class="attachment-name" :title="file.name">
|
||||||
|
{{ file.name }}
|
||||||
|
</div>
|
||||||
|
<a-tag v-if="!canPreview(file)" color="default" class="attachment-tag">暂不支持预览</a-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-empty v-else description="暂无附件" />
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
<preview v-else ref="previewRef" @goBack="handlePreviewClose" />
|
||||||
|
<stage-add-dialog ref="formRef" @successful="fetchDetail"/>
|
||||||
|
<fj-upload-form ref="uploadFormRef" @successful="fetchAttachments"/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="Description">
|
<script setup lang="ts" name="Description">
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import projectApi from '@/api/pmm/projectApi'
|
import projectApi from '@/api/pmm/projectApi'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import StageAddDialog from "@/views/pmm/project/request/stageAddDialog.vue";
|
import StageAddDialog from "@/views/pmm/project/request/stageAddDialog.vue";
|
||||||
|
import Preview from '@/views/dev/file/preview.vue'
|
||||||
|
import FjUploadForm from "@/views/pmm/project/request/fjUploadForm.vue";
|
||||||
|
|
||||||
|
interface AttachmentItem {
|
||||||
|
id: number | string
|
||||||
|
name: string
|
||||||
|
suffix: string
|
||||||
|
downloadPath: string
|
||||||
|
thumbnail?: string
|
||||||
|
}
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|
||||||
@@ -158,6 +199,11 @@
|
|||||||
const stageList = ref<any[]>([])
|
const stageList = ref<any[]>([])
|
||||||
const plannedHours = ref(0)
|
const plannedHours = ref(0)
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
|
const previewRef = ref()
|
||||||
|
const uploadFormRef = ref()
|
||||||
|
const previewVisible = ref(false)
|
||||||
|
|
||||||
|
const attachmentList = ref<AttachmentItem[]>([])
|
||||||
|
|
||||||
const parsedQuery = computed(() => {
|
const parsedQuery = computed(() => {
|
||||||
const payload = route.query.item as string | undefined
|
const payload = route.query.item as string | undefined
|
||||||
@@ -316,9 +362,156 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(projectId, () => {
|
const resolveAttachmentSource = (payload: unknown) => {
|
||||||
fetchDetail()
|
if (Array.isArray(payload)) {
|
||||||
}, { immediate: true })
|
return payload
|
||||||
|
}
|
||||||
|
if (payload && typeof payload === 'object') {
|
||||||
|
const obj = payload as Record<string, any>
|
||||||
|
const candidateKeys = ['data', 'records', 'rows', 'list']
|
||||||
|
for (const key of candidateKeys) {
|
||||||
|
if (Array.isArray(obj[key])) {
|
||||||
|
return obj[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const getSuffixFromName = (name?: string) => {
|
||||||
|
if (!name || typeof name !== 'string') {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const lastDotIndex = name.lastIndexOf('.')
|
||||||
|
if (lastDotIndex === -1) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return name.slice(lastDotIndex + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeAttachmentRecord = (raw: Record<string, any>): AttachmentItem | null => {
|
||||||
|
if (!raw) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const name =
|
||||||
|
raw.name ||
|
||||||
|
raw.fileName ||
|
||||||
|
raw.originName ||
|
||||||
|
raw.originalName ||
|
||||||
|
raw.fileOriginalName ||
|
||||||
|
raw.attachmentName ||
|
||||||
|
raw.title
|
||||||
|
const downloadPath =
|
||||||
|
raw.downloadPath ||
|
||||||
|
raw.fileUrl ||
|
||||||
|
raw.url ||
|
||||||
|
raw.downloadUrl ||
|
||||||
|
raw.path ||
|
||||||
|
raw.filePath ||
|
||||||
|
raw.storagePath
|
||||||
|
if (!downloadPath) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const suffixSource =
|
||||||
|
raw.suffix ||
|
||||||
|
raw.fileSuffix ||
|
||||||
|
raw.fileType ||
|
||||||
|
getSuffixFromName(name) ||
|
||||||
|
getSuffixFromName(downloadPath)
|
||||||
|
const suffix = typeof suffixSource === 'string' ? suffixSource.toLowerCase() : ''
|
||||||
|
const thumbnail = raw.thumbnail || raw.thumbUrl || raw.previewUrl || raw.cover
|
||||||
|
const id = raw.id || raw.fileId || raw.attachmentId || raw.uid || downloadPath || name || Date.now()
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name: name || '未命名文件',
|
||||||
|
suffix,
|
||||||
|
downloadPath,
|
||||||
|
thumbnail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchAttachments = () => {
|
||||||
|
if (!projectId.value) {
|
||||||
|
attachmentList.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
projectApi
|
||||||
|
.getAttachment({ projectId: projectId.value })
|
||||||
|
.then((response) => {
|
||||||
|
const source = resolveAttachmentSource(response)
|
||||||
|
if (!source.length) {
|
||||||
|
attachmentList.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parsed = source
|
||||||
|
.map((item) => normalizeAttachmentRecord(item))
|
||||||
|
.filter((item): item is AttachmentItem => Boolean(item))
|
||||||
|
if (!parsed.length) {
|
||||||
|
message.warning('附件数据格式错误')
|
||||||
|
}
|
||||||
|
attachmentList.value = parsed
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.warn('获取项目附件失败', error)
|
||||||
|
message.error('获取项目附件失败')
|
||||||
|
attachmentList.value = []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(projectId, () => {
|
||||||
|
fetchDetail()
|
||||||
|
fetchAttachments()
|
||||||
|
}, { immediate: true })
|
||||||
|
|
||||||
|
const previewableSuffix = ['doc', 'docx', 'xls', 'xlsx', 'pdf', 'jpg', 'png', 'gif', 'svg', 'ico', 'tmp', 'jpeg']
|
||||||
|
|
||||||
|
const getFileIcon = (suffix: string) => {
|
||||||
|
const lower = suffix?.toLowerCase()
|
||||||
|
const iconMap: Record<string, string> = {
|
||||||
|
doc: '/src/assets/images/fileImg/docx.png',
|
||||||
|
docx: '/src/assets/images/fileImg/docx.png',
|
||||||
|
xls: '/src/assets/images/fileImg/xlsx.png',
|
||||||
|
xlsx: '/src/assets/images/fileImg/xlsx.png',
|
||||||
|
ppt: '/src/assets/images/fileImg/ppt.png',
|
||||||
|
pptx: '/src/assets/images/fileImg/ppt.png',
|
||||||
|
pdf: '/src/assets/images/fileImg/pdf.png',
|
||||||
|
txt: '/src/assets/images/fileImg/txt.png',
|
||||||
|
zip: '/src/assets/images/fileImg/zip.png',
|
||||||
|
rar: '/src/assets/images/fileImg/rar.png',
|
||||||
|
html: '/src/assets/images/fileImg/html.png'
|
||||||
|
}
|
||||||
|
return iconMap[lower] || '/src/assets/images/fileImg/file.png'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAttachmentThumbnail = (file: AttachmentItem) => {
|
||||||
|
if (file.thumbnail) {
|
||||||
|
return file.thumbnail
|
||||||
|
}
|
||||||
|
const imgSuffix = ['png', 'jpg', 'jpeg', 'gif', 'bmp']
|
||||||
|
if (imgSuffix.indexOf(file.suffix?.toLowerCase()) > -1) {
|
||||||
|
return file.downloadPath
|
||||||
|
}
|
||||||
|
return getFileIcon(file.suffix)
|
||||||
|
}
|
||||||
|
|
||||||
|
const canPreview = (file: AttachmentItem) => {
|
||||||
|
return previewableSuffix.indexOf(file.suffix?.toLowerCase()) > -1
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePreview = (file: AttachmentItem) => {
|
||||||
|
if (!canPreview(file)) {
|
||||||
|
message.warning('当前文件暂不支持在线预览')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
previewVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
previewRef.value?.onOpen(file)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePreviewClose = () => {
|
||||||
|
previewVisible.value = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -440,4 +633,69 @@
|
|||||||
.action-btn {
|
.action-btn {
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.attachments-section {
|
||||||
|
margin-top: 32px;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
padding-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachments-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-item {
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-item:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-thumb {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
margin: 0 auto 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-thumb img {
|
||||||
|
max-width: 80px;
|
||||||
|
max-height: 80px;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-name {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #333;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-tag {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
156
nl-vue/src/views/pmm/project/request/fjUploadForm.vue
Normal file
156
nl-vue/src/views/pmm/project/request/fjUploadForm.vue
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<xn-form-container
|
||||||
|
title="文件上传"
|
||||||
|
:width="550"
|
||||||
|
:visible="visible"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:bodyStyle="{ 'padding-top': '0px' }"
|
||||||
|
@close="onClose"
|
||||||
|
>
|
||||||
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
|
<a-tab-pane key="Local" tab="本地">
|
||||||
|
<a-spin :spinning="uploadLoading">
|
||||||
|
<a-upload-dragger :show-upload-list="false" :custom-request="customRequestLocal">
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<inbox-outlined></inbox-outlined>
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||||
|
<p class="ant-upload-hint">支持单个上传</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
</a-spin>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="Aliyun" tab="阿里云">
|
||||||
|
<a-spin :spinning="uploadLoading">
|
||||||
|
<a-upload-dragger :custom-request="customRequestAliyun" :show-upload-list="false">
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<inbox-outlined></inbox-outlined>
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||||
|
<p class="ant-upload-hint">支持单个上传</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
</a-spin>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="Tencent" tab="腾讯云">
|
||||||
|
<a-spin :spinning="uploadLoading">
|
||||||
|
<a-upload-dragger :custom-request="customRequestTencent" :show-upload-list="false">
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<inbox-outlined></inbox-outlined>
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||||
|
<p class="ant-upload-hint">支持单个上传</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
</a-spin>
|
||||||
|
</a-tab-pane>
|
||||||
|
<a-tab-pane key="Minio" tab="MINIO">
|
||||||
|
<a-spin :spinning="uploadLoading">
|
||||||
|
<a-upload-dragger :custom-request="customRequestMinio" :show-upload-list="false">
|
||||||
|
<p class="ant-upload-drag-icon">
|
||||||
|
<inbox-outlined></inbox-outlined>
|
||||||
|
</p>
|
||||||
|
<p class="ant-upload-text">单击或拖动文件到此区域进行上传</p>
|
||||||
|
<p class="ant-upload-hint">支持单个上传</p>
|
||||||
|
</a-upload-dragger>
|
||||||
|
</a-spin>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</xn-form-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="uploadForm">
|
||||||
|
import fileApi from '@/api/dev/fileApi'
|
||||||
|
import projectFileApi from '@/api/pmm/projectFileApi'
|
||||||
|
// 定义emit事件
|
||||||
|
const emit = defineEmits({ successful: null })
|
||||||
|
// 默认是关闭状态
|
||||||
|
const visible = ref(false)
|
||||||
|
const activeKey = ref('Local')
|
||||||
|
const uploadLoading = ref(false)
|
||||||
|
const projectId = ref('')
|
||||||
|
// 打开抽屉
|
||||||
|
const openUpload = (record) => {
|
||||||
|
projectId.value = record
|
||||||
|
visible.value = true
|
||||||
|
}
|
||||||
|
// 关闭抽屉
|
||||||
|
const onClose = () => {
|
||||||
|
visible.value = false
|
||||||
|
projectId.value = ''
|
||||||
|
emit('successful')
|
||||||
|
}
|
||||||
|
// 上传本地文件
|
||||||
|
const customRequestLocal = (data) => {
|
||||||
|
uploadLoading.value = true
|
||||||
|
const fileData = new FormData()
|
||||||
|
fileData.append('file', data.file)
|
||||||
|
fileApi
|
||||||
|
.fileUploadLocalReturnUrl(fileData)
|
||||||
|
.then((res) => {
|
||||||
|
bindProjectFile(res).then(() => {
|
||||||
|
emit('successful')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
uploadLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 上传阿里云文件
|
||||||
|
const customRequestAliyun = (data) => {
|
||||||
|
uploadLoading.value = true
|
||||||
|
const fileData = new FormData()
|
||||||
|
fileData.append('file', data.file)
|
||||||
|
fileApi
|
||||||
|
.fileUploadAliyunReturnUrl(fileData)
|
||||||
|
.then((res) => {
|
||||||
|
bindProjectFile(res).then(() => {
|
||||||
|
emit('successful')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
uploadLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 上传腾讯文件
|
||||||
|
const customRequestTencent = (data) => {
|
||||||
|
uploadLoading.value = true
|
||||||
|
const fileData = new FormData()
|
||||||
|
fileData.append('file', data.file)
|
||||||
|
fileApi
|
||||||
|
.fileUploadTencentReturnUrl(fileData)
|
||||||
|
.then((res) => {
|
||||||
|
bindProjectFile(res).then(() => {
|
||||||
|
emit('successful')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
uploadLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 上传Minio文件
|
||||||
|
const customRequestMinio = (data) => {
|
||||||
|
uploadLoading.value = true
|
||||||
|
const fileData = new FormData()
|
||||||
|
fileData.append('file', data.file)
|
||||||
|
fileApi
|
||||||
|
.fileUploadMinioReturnUrl(fileData)
|
||||||
|
.then((res) => {
|
||||||
|
bindProjectFile(res).then(() => {
|
||||||
|
emit('successful')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
uploadLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 绑定项目与文件
|
||||||
|
const bindProjectFile = (fileUrl) => {
|
||||||
|
const param = {
|
||||||
|
projectId: projectId.value,
|
||||||
|
fileAddress: fileUrl
|
||||||
|
}
|
||||||
|
return projectFileApi.bindProjectFile(param)
|
||||||
|
}
|
||||||
|
// 调用这个函数将子组件的一些数据和方法暴露出去
|
||||||
|
defineExpose({
|
||||||
|
openUpload
|
||||||
|
})
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user