feat(测试跟踪): 用例及缺陷附件支持文件管理
This commit is contained in:
parent
d359bbf66f
commit
c0870f06e5
|
@ -27,6 +27,7 @@
|
||||||
<xmlbeans.version>5.1.0</xmlbeans.version>
|
<xmlbeans.version>5.1.0</xmlbeans.version>
|
||||||
<poi.version>5.1.0</poi.version>
|
<poi.version>5.1.0</poi.version>
|
||||||
<jgit.version>6.2.0.202206071550-r</jgit.version>
|
<jgit.version>6.2.0.202206071550-r</jgit.version>
|
||||||
|
<commons-fileupload.version>1.3</commons-fileupload.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -152,6 +153,17 @@
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
<artifactId>commons-codec</artifactId>
|
<artifactId>commons-codec</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-fileupload</groupId>
|
||||||
|
<artifactId>commons-fileupload</artifactId>
|
||||||
|
<version>${commons-fileupload.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
|
|
|
@ -12,5 +12,7 @@ public class AttachmentModuleRelation implements Serializable {
|
||||||
|
|
||||||
private String attachmentId;
|
private String attachmentId;
|
||||||
|
|
||||||
|
private String fileMetadataRefId;
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
}
|
}
|
|
@ -313,6 +313,76 @@ public class AttachmentModuleRelationExample {
|
||||||
addCriterion("attachment_id not between", value1, value2, "attachmentId");
|
addCriterion("attachment_id not between", value1, value2, "attachmentId");
|
||||||
return (Criteria) this;
|
return (Criteria) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdIsNull() {
|
||||||
|
addCriterion("file_metadata_ref_id is null");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdIsNotNull() {
|
||||||
|
addCriterion("file_metadata_ref_id is not null");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdEqualTo(String value) {
|
||||||
|
addCriterion("file_metadata_ref_id =", value, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdNotEqualTo(String value) {
|
||||||
|
addCriterion("file_metadata_ref_id <>", value, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdGreaterThan(String value) {
|
||||||
|
addCriterion("file_metadata_ref_id >", value, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdGreaterThanOrEqualTo(String value) {
|
||||||
|
addCriterion("file_metadata_ref_id >=", value, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdLessThan(String value) {
|
||||||
|
addCriterion("file_metadata_ref_id <", value, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdLessThanOrEqualTo(String value) {
|
||||||
|
addCriterion("file_metadata_ref_id <=", value, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdLike(String value) {
|
||||||
|
addCriterion("file_metadata_ref_id like", value, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdNotLike(String value) {
|
||||||
|
addCriterion("file_metadata_ref_id not like", value, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdIn(List<String> values) {
|
||||||
|
addCriterion("file_metadata_ref_id in", values, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdNotIn(List<String> values) {
|
||||||
|
addCriterion("file_metadata_ref_id not in", values, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdBetween(String value1, String value2) {
|
||||||
|
addCriterion("file_metadata_ref_id between", value1, value2, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Criteria andFileMetadataRefIdNotBetween(String value1, String value2) {
|
||||||
|
addCriterion("file_metadata_ref_id not between", value1, value2, "fileMetadataRefId");
|
||||||
|
return (Criteria) this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Criteria extends GeneratedCriteria {
|
public static class Criteria extends GeneratedCriteria {
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package io.metersphere.base.domain;
|
package io.metersphere.base.domain;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class FileAttachmentMetadata implements Serializable {
|
public class FileAttachmentMetadata implements Serializable {
|
||||||
private String id;
|
private String id;
|
||||||
|
@ -21,5 +22,9 @@ public class FileAttachmentMetadata implements Serializable {
|
||||||
|
|
||||||
private String filePath;
|
private String filePath;
|
||||||
|
|
||||||
|
private Boolean isLocal;
|
||||||
|
|
||||||
|
private Boolean isRelatedDeleted;
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
}
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
<result column="relation_id" jdbcType="VARCHAR" property="relationId" />
|
<result column="relation_id" jdbcType="VARCHAR" property="relationId" />
|
||||||
<result column="relation_type" jdbcType="VARCHAR" property="relationType" />
|
<result column="relation_type" jdbcType="VARCHAR" property="relationType" />
|
||||||
<result column="attachment_id" jdbcType="VARCHAR" property="attachmentId" />
|
<result column="attachment_id" jdbcType="VARCHAR" property="attachmentId" />
|
||||||
|
<result column="file_metadata_ref_id" jdbcType="VARCHAR" property="fileMetadataRefId" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<sql id="Example_Where_Clause">
|
<sql id="Example_Where_Clause">
|
||||||
<where>
|
<where>
|
||||||
|
@ -65,7 +66,7 @@
|
||||||
</where>
|
</where>
|
||||||
</sql>
|
</sql>
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
relation_id, relation_type, attachment_id
|
relation_id, relation_type, attachment_id, file_metadata_ref_id
|
||||||
</sql>
|
</sql>
|
||||||
<select id="selectByExample" parameterType="io.metersphere.base.domain.AttachmentModuleRelationExample" resultMap="BaseResultMap">
|
<select id="selectByExample" parameterType="io.metersphere.base.domain.AttachmentModuleRelationExample" resultMap="BaseResultMap">
|
||||||
select
|
select
|
||||||
|
@ -88,10 +89,10 @@
|
||||||
</if>
|
</if>
|
||||||
</delete>
|
</delete>
|
||||||
<insert id="insert" parameterType="io.metersphere.base.domain.AttachmentModuleRelation">
|
<insert id="insert" parameterType="io.metersphere.base.domain.AttachmentModuleRelation">
|
||||||
insert into attachment_module_relation (relation_id, relation_type, attachment_id
|
insert into attachment_module_relation (relation_id, relation_type, attachment_id,
|
||||||
)
|
file_metadata_ref_id)
|
||||||
values (#{relationId,jdbcType=VARCHAR}, #{relationType,jdbcType=VARCHAR}, #{attachmentId,jdbcType=VARCHAR}
|
values (#{relationId,jdbcType=VARCHAR}, #{relationType,jdbcType=VARCHAR}, #{attachmentId,jdbcType=VARCHAR},
|
||||||
)
|
#{fileMetadataRefId,jdbcType=VARCHAR})
|
||||||
</insert>
|
</insert>
|
||||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.AttachmentModuleRelation">
|
<insert id="insertSelective" parameterType="io.metersphere.base.domain.AttachmentModuleRelation">
|
||||||
insert into attachment_module_relation
|
insert into attachment_module_relation
|
||||||
|
@ -105,6 +106,9 @@
|
||||||
<if test="attachmentId != null">
|
<if test="attachmentId != null">
|
||||||
attachment_id,
|
attachment_id,
|
||||||
</if>
|
</if>
|
||||||
|
<if test="fileMetadataRefId != null">
|
||||||
|
file_metadata_ref_id,
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="relationId != null">
|
<if test="relationId != null">
|
||||||
|
@ -116,6 +120,9 @@
|
||||||
<if test="attachmentId != null">
|
<if test="attachmentId != null">
|
||||||
#{attachmentId,jdbcType=VARCHAR},
|
#{attachmentId,jdbcType=VARCHAR},
|
||||||
</if>
|
</if>
|
||||||
|
<if test="fileMetadataRefId != null">
|
||||||
|
#{fileMetadataRefId,jdbcType=VARCHAR},
|
||||||
|
</if>
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
<select id="countByExample" parameterType="io.metersphere.base.domain.AttachmentModuleRelationExample" resultType="java.lang.Long">
|
<select id="countByExample" parameterType="io.metersphere.base.domain.AttachmentModuleRelationExample" resultType="java.lang.Long">
|
||||||
|
@ -136,6 +143,9 @@
|
||||||
<if test="record.attachmentId != null">
|
<if test="record.attachmentId != null">
|
||||||
attachment_id = #{record.attachmentId,jdbcType=VARCHAR},
|
attachment_id = #{record.attachmentId,jdbcType=VARCHAR},
|
||||||
</if>
|
</if>
|
||||||
|
<if test="record.fileMetadataRefId != null">
|
||||||
|
file_metadata_ref_id = #{record.fileMetadataRefId,jdbcType=VARCHAR},
|
||||||
|
</if>
|
||||||
</set>
|
</set>
|
||||||
<if test="_parameter != null">
|
<if test="_parameter != null">
|
||||||
<include refid="Update_By_Example_Where_Clause" />
|
<include refid="Update_By_Example_Where_Clause" />
|
||||||
|
@ -145,7 +155,8 @@
|
||||||
update attachment_module_relation
|
update attachment_module_relation
|
||||||
set relation_id = #{record.relationId,jdbcType=VARCHAR},
|
set relation_id = #{record.relationId,jdbcType=VARCHAR},
|
||||||
relation_type = #{record.relationType,jdbcType=VARCHAR},
|
relation_type = #{record.relationType,jdbcType=VARCHAR},
|
||||||
attachment_id = #{record.attachmentId,jdbcType=VARCHAR}
|
attachment_id = #{record.attachmentId,jdbcType=VARCHAR},
|
||||||
|
file_metadata_ref_id = #{record.fileMetadataRefId,jdbcType=VARCHAR}
|
||||||
<if test="_parameter != null">
|
<if test="_parameter != null">
|
||||||
<include refid="Update_By_Example_Where_Clause" />
|
<include refid="Update_By_Example_Where_Clause" />
|
||||||
</if>
|
</if>
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
<mapper namespace="io.metersphere.base.mapper.ext.ExtAttachmentModuleRelationMapper">
|
<mapper namespace="io.metersphere.base.mapper.ext.ExtAttachmentModuleRelationMapper">
|
||||||
<insert id="batchInsert" parameterType="java.util.List">
|
<insert id="batchInsert" parameterType="java.util.List">
|
||||||
INSERT INTO
|
INSERT INTO
|
||||||
attachment_module_relation (relation_id, relation_type, attachment_id)
|
attachment_module_relation (relation_id, relation_type, attachment_id, file_metadata_ref_id)
|
||||||
VALUES
|
VALUES
|
||||||
<foreach collection="attachmentModuleRelations" item="relation" separator="," >
|
<foreach collection="attachmentModuleRelations" item="relation" separator="," >
|
||||||
(#{relation.relationId}, #{relation.relationType}, #{relation.attachmentId})
|
(#{relation.relationId}, #{relation.relationType}, #{relation.attachmentId}, #{relation.fileMetadataRefId})
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
</mapper>
|
</mapper>
|
|
@ -1,5 +1,5 @@
|
||||||
package io.metersphere.commons.constants;
|
package io.metersphere.commons.constants;
|
||||||
|
|
||||||
public enum FileAssociationType {
|
public enum FileAssociationType {
|
||||||
API, CASE, SCENARIO, UI, ENVIRONMENT
|
API, CASE, SCENARIO, UI, ENVIRONMENT, TEST_CASE, ISSUE
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.metersphere.metadata.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class AttachmentDumpRequest extends DumpFileRequest{
|
||||||
|
private String attachmentId;
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.*;
|
import io.metersphere.commons.utils.*;
|
||||||
import io.metersphere.performance.request.QueryProjectFileRequest;
|
import io.metersphere.performance.request.QueryProjectFileRequest;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import io.metersphere.xmind.utils.FileUtil;
|
||||||
import org.apache.commons.lang3.SerializationUtils;
|
import org.apache.commons.lang3.SerializationUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -50,6 +51,12 @@ public class FileService {
|
||||||
return FileUtils.fileToByte(attachmentFile);
|
return FileUtils.fileToByte(attachmentFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public MultipartFile getAttachmentMultipartFile(String id) {
|
||||||
|
FileAttachmentMetadata fileAttachmentMetadata = fileAttachmentMetadataMapper.selectByPrimaryKey(id);
|
||||||
|
File attachmentFile = new File(fileAttachmentMetadata.getFilePath() + "/" + fileAttachmentMetadata.getName());
|
||||||
|
return FileUtil.fileToMultipartFile(attachmentFile);
|
||||||
|
}
|
||||||
|
|
||||||
public FileContent getFileContent(String fileId) {
|
public FileContent getFileContent(String fileId) {
|
||||||
return fileContentMapper.selectByPrimaryKey(fileId);
|
return fileContentMapper.selectByPrimaryKey(fileId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package io.metersphere.track.controller;
|
package io.metersphere.track.controller;
|
||||||
|
|
||||||
import io.metersphere.base.domain.FileAttachmentMetadata;
|
import io.metersphere.base.domain.FileAttachmentMetadata;
|
||||||
|
import io.metersphere.metadata.service.FileMetadataService;
|
||||||
|
import io.metersphere.metadata.vo.AttachmentDumpRequest;
|
||||||
import io.metersphere.service.FileService;
|
import io.metersphere.service.FileService;
|
||||||
import io.metersphere.track.request.attachment.AttachmentRequest;
|
import io.metersphere.track.request.attachment.AttachmentRequest;
|
||||||
import io.metersphere.track.request.testplan.FileOperationRequest;
|
import io.metersphere.track.request.testplan.FileOperationRequest;
|
||||||
|
@ -14,6 +16,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,15 +30,23 @@ public class AttachmentController {
|
||||||
private FileService fileService;
|
private FileService fileService;
|
||||||
@Resource
|
@Resource
|
||||||
private AttachmentService attachmentService;
|
private AttachmentService attachmentService;
|
||||||
|
@Resource
|
||||||
|
private FileMetadataService fileMetadataService;
|
||||||
|
|
||||||
@PostMapping(value = "/upload", consumes = {"multipart/form-data"})
|
@PostMapping(value = "/upload", consumes = {"multipart/form-data"})
|
||||||
public void uploadAttachment(@RequestPart("request") AttachmentRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
public void uploadAttachment(@RequestPart("request") AttachmentRequest request, @RequestPart(value = "file", required = false) MultipartFile file) {
|
||||||
attachmentService.uploadAttachment(request, file);
|
attachmentService.uploadAttachment(request, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/preview/{fileId}")
|
@GetMapping("/preview/{fileId}/{isLocal}")
|
||||||
public ResponseEntity<byte[]> previewAttachment(@PathVariable String fileId) {
|
public ResponseEntity<byte[]> previewAttachment(@PathVariable String fileId, @PathVariable Boolean isLocal) {
|
||||||
byte[] bytes = fileService.getAttachmentBytes(fileId);
|
byte[] bytes;
|
||||||
|
if (isLocal) {
|
||||||
|
bytes = fileService.getAttachmentBytes(fileId);
|
||||||
|
} else {
|
||||||
|
String refId = attachmentService.getRefIdByAttachmentId(fileId);
|
||||||
|
bytes = fileMetadataService.loadFileAsBytes(refId);
|
||||||
|
}
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileId + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileId + "\"")
|
||||||
|
@ -44,7 +55,13 @@ public class AttachmentController {
|
||||||
|
|
||||||
@PostMapping("/download")
|
@PostMapping("/download")
|
||||||
public ResponseEntity<byte[]> downloadAttachment(@RequestBody FileOperationRequest fileOperationRequest) {
|
public ResponseEntity<byte[]> downloadAttachment(@RequestBody FileOperationRequest fileOperationRequest) {
|
||||||
byte[] bytes = fileService.getAttachmentBytes(fileOperationRequest.getId());
|
byte[] bytes;
|
||||||
|
if (fileOperationRequest.getIsLocal()) {
|
||||||
|
bytes = fileService.getAttachmentBytes(fileOperationRequest.getId());
|
||||||
|
} else {
|
||||||
|
String refId = attachmentService.getRefIdByAttachmentId(fileOperationRequest.getId());
|
||||||
|
bytes = fileMetadataService.loadFileAsBytes(refId);
|
||||||
|
}
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileOperationRequest.getName(), StandardCharsets.UTF_8) + "\"")
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + URLEncoder.encode(fileOperationRequest.getName(), StandardCharsets.UTF_8) + "\"")
|
||||||
|
@ -61,4 +78,22 @@ public class AttachmentController {
|
||||||
public List<FileAttachmentMetadata> listMetadata(@RequestBody AttachmentRequest request) {
|
public List<FileAttachmentMetadata> listMetadata(@RequestBody AttachmentRequest request) {
|
||||||
return attachmentService.listMetadata(request);
|
return attachmentService.listMetadata(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/metadata/relate")
|
||||||
|
public void relate(@RequestBody AttachmentRequest request) {
|
||||||
|
attachmentService.relate(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/metadata/unrelated")
|
||||||
|
public void unrelated(@RequestBody AttachmentRequest request) {
|
||||||
|
attachmentService.unrelated(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping(value = "/metadata/dump")
|
||||||
|
public void dumpFile(@RequestBody AttachmentDumpRequest request) {
|
||||||
|
List<MultipartFile> files = new ArrayList<>();
|
||||||
|
MultipartFile file = fileService.getAttachmentMultipartFile(request.getAttachmentId());
|
||||||
|
files.add(file);
|
||||||
|
fileMetadataService.dumpFile(request, files);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package io.metersphere.track.issue;
|
package io.metersphere.track.issue;
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import com.alibaba.fastjson.JSONArray;
|
import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import io.metersphere.base.domain.*;
|
import io.metersphere.base.domain.*;
|
||||||
|
|
|
@ -2,6 +2,8 @@ package io.metersphere.track.request.attachment;
|
||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author songcc
|
* @author songcc
|
||||||
|
@ -14,4 +16,6 @@ public class AttachmentRequest {
|
||||||
private String belongId;
|
private String belongId;
|
||||||
|
|
||||||
private String copyBelongId;
|
private String copyBelongId;
|
||||||
|
|
||||||
|
private List<String> metadataRefIds;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@ public class EditTestCaseRequest extends TestCaseWithBLOBs {
|
||||||
private String copyCaseId;
|
private String copyCaseId;
|
||||||
// 是否处理附件文件
|
// 是否处理附件文件
|
||||||
private boolean handleAttachment = true;
|
private boolean handleAttachment = true;
|
||||||
|
// 关联文件管理引用ID
|
||||||
|
private List<String> relateFileMetaIds = new ArrayList<>();
|
||||||
|
// 取消关联文件应用ID
|
||||||
|
private List<String> unRelateFileMetaIds = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建新版本时 是否连带复制其他信息的配置类
|
* 创建新版本时 是否连带复制其他信息的配置类
|
||||||
|
|
|
@ -8,6 +8,7 @@ import io.metersphere.track.dto.PlatformStatusDTO;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -53,4 +54,8 @@ public class IssuesUpdateRequest extends IssuesWithBLOBs {
|
||||||
* 复制缺陷时原始缺陷ID
|
* 复制缺陷时原始缺陷ID
|
||||||
*/
|
*/
|
||||||
private String copyIssueId;
|
private String copyIssueId;
|
||||||
|
// 关联文件管理引用ID
|
||||||
|
private List<String> relateFileMetaIds = new ArrayList<>();
|
||||||
|
// 取消关联文件应用ID
|
||||||
|
private List<String> unRelateFileMetaIds = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,4 +8,5 @@ import lombok.Setter;
|
||||||
public class FileOperationRequest {
|
public class FileOperationRequest {
|
||||||
private String id;
|
private String id;
|
||||||
private String name;
|
private String name;
|
||||||
|
private Boolean isLocal;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,24 +5,31 @@ import io.metersphere.base.mapper.*;
|
||||||
import io.metersphere.base.mapper.ext.ExtAttachmentModuleRelationMapper;
|
import io.metersphere.base.mapper.ext.ExtAttachmentModuleRelationMapper;
|
||||||
import io.metersphere.commons.constants.AttachmentSyncType;
|
import io.metersphere.commons.constants.AttachmentSyncType;
|
||||||
import io.metersphere.commons.constants.AttachmentType;
|
import io.metersphere.commons.constants.AttachmentType;
|
||||||
|
import io.metersphere.commons.constants.FileAssociationType;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.commons.utils.BeanUtils;
|
||||||
|
import io.metersphere.commons.utils.FileUtils;
|
||||||
import io.metersphere.commons.utils.SessionUtils;
|
import io.metersphere.commons.utils.SessionUtils;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
|
import io.metersphere.metadata.service.FileMetadataService;
|
||||||
import io.metersphere.service.FileService;
|
import io.metersphere.service.FileService;
|
||||||
import io.metersphere.track.issue.IssueFactory;
|
import io.metersphere.track.issue.IssueFactory;
|
||||||
import io.metersphere.track.request.attachment.AttachmentRequest;
|
import io.metersphere.track.request.attachment.AttachmentRequest;
|
||||||
import io.metersphere.track.request.testcase.IssuesRequest;
|
import io.metersphere.track.request.testcase.IssuesRequest;
|
||||||
import io.metersphere.track.request.testcase.IssuesUpdateRequest;
|
import io.metersphere.track.request.testcase.IssuesUpdateRequest;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.ibatis.session.ExecutorType;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
|
import org.mybatis.spring.SqlSessionUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,6 +55,14 @@ public class AttachmentService {
|
||||||
private AttachmentModuleRelationMapper attachmentModuleRelationMapper;
|
private AttachmentModuleRelationMapper attachmentModuleRelationMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ExtAttachmentModuleRelationMapper extAttachmentModuleRelationMapper;
|
private ExtAttachmentModuleRelationMapper extAttachmentModuleRelationMapper;
|
||||||
|
@Resource
|
||||||
|
private FileMetadataMapper fileMetadataMapper;
|
||||||
|
@Resource
|
||||||
|
private FileAssociationMapper fileAssociationMapper;
|
||||||
|
@Resource
|
||||||
|
private FileMetadataService fileMetadataService;
|
||||||
|
@Resource
|
||||||
|
SqlSessionFactory sqlSessionFactory;
|
||||||
|
|
||||||
public void uploadAttachment(AttachmentRequest request, MultipartFile file) {
|
public void uploadAttachment(AttachmentRequest request, MultipartFile file) {
|
||||||
// 附件上传的前置校验
|
// 附件上传的前置校验
|
||||||
|
@ -129,25 +144,162 @@ public class AttachmentService {
|
||||||
example.createCriteria().andRelationIdEqualTo(request.getCopyBelongId()).andRelationTypeEqualTo(request.getBelongType());
|
example.createCriteria().andRelationIdEqualTo(request.getCopyBelongId()).andRelationTypeEqualTo(request.getBelongType());
|
||||||
List<AttachmentModuleRelation> attachmentModuleRelations = attachmentModuleRelationMapper.selectByExample(example);
|
List<AttachmentModuleRelation> attachmentModuleRelations = attachmentModuleRelationMapper.selectByExample(example);
|
||||||
if (CollectionUtils.isNotEmpty(attachmentModuleRelations)) {
|
if (CollectionUtils.isNotEmpty(attachmentModuleRelations)) {
|
||||||
attachmentModuleRelations.forEach(attachmentModuleRelation -> {
|
// 本地附件
|
||||||
FileAttachmentMetadata fileAttachmentMetadata = fileService.copyAttachment(attachmentModuleRelation.getAttachmentId(), request.getBelongType(), request.getBelongId());
|
List<String> localAttachments = attachmentModuleRelations.stream()
|
||||||
|
.filter(relation -> StringUtils.isEmpty(relation.getFileMetadataRefId()))
|
||||||
|
.map(AttachmentModuleRelation::getAttachmentId)
|
||||||
|
.filter(StringUtils::isNotEmpty).collect(Collectors.toList());
|
||||||
|
localAttachments.forEach(localAttachmentId -> {
|
||||||
|
FileAttachmentMetadata fileAttachmentMetadata = fileService.copyAttachment(localAttachmentId, request.getBelongType(), request.getBelongId());
|
||||||
AttachmentModuleRelation record = new AttachmentModuleRelation();
|
AttachmentModuleRelation record = new AttachmentModuleRelation();
|
||||||
record.setRelationId(request.getBelongId());
|
record.setRelationId(request.getBelongId());
|
||||||
record.setRelationType(request.getBelongType());
|
record.setRelationType(request.getBelongType());
|
||||||
record.setAttachmentId(fileAttachmentMetadata.getId());
|
record.setAttachmentId(fileAttachmentMetadata.getId());
|
||||||
attachmentModuleRelationMapper.insert(record);
|
attachmentModuleRelationMapper.insert(record);
|
||||||
});
|
});
|
||||||
|
// 文件管理关联附件
|
||||||
|
List<AttachmentModuleRelation> refAttachments = attachmentModuleRelations.stream()
|
||||||
|
.filter(relation -> StringUtils.isNotEmpty(relation.getFileMetadataRefId())).collect(Collectors.toList());
|
||||||
|
refAttachments.forEach(refAttachment -> {
|
||||||
|
refAttachment.setRelationId(request.getBelongId());
|
||||||
|
attachmentModuleRelationMapper.insert(refAttachment);
|
||||||
|
// 缺陷类型的附件, 关联时需单独同步第三方平台
|
||||||
|
if (AttachmentType.ISSUE.type().equals(request.getBelongType())) {
|
||||||
|
String metadataRefId = getRefIdByAttachmentId(refAttachment.getAttachmentId());
|
||||||
|
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(metadataRefId);
|
||||||
|
IssuesWithBLOBs issues = issuesMapper.selectByPrimaryKey(request.getBelongId());
|
||||||
|
IssuesUpdateRequest updateRequest = new IssuesUpdateRequest();
|
||||||
|
updateRequest.setPlatformId(issues.getPlatformId());
|
||||||
|
File refFile = downloadMetadataFile(metadataRefId, fileMetadata.getName());
|
||||||
|
IssuesRequest issuesRequest = new IssuesRequest();
|
||||||
|
issuesRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||||
|
Objects.requireNonNull(IssueFactory.createPlatform(issues.getPlatform(), issuesRequest))
|
||||||
|
.syncIssuesAttachment(updateRequest, refFile, AttachmentSyncType.UPLOAD);
|
||||||
|
FileUtils.deleteFile(FileUtils.ATTACHMENT_TMP_DIR + File.separator + fileMetadata.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FileAttachmentMetadata> listMetadata(AttachmentRequest request) {
|
public List<FileAttachmentMetadata> listMetadata(AttachmentRequest request) {
|
||||||
List<String> attachmentIds = getAttachmentIdsByParam(request);
|
List<FileAttachmentMetadata> attachments = new ArrayList<FileAttachmentMetadata>();
|
||||||
if (CollectionUtils.isEmpty(attachmentIds)) {
|
AttachmentModuleRelationExample example = new AttachmentModuleRelationExample();
|
||||||
return new ArrayList<>();
|
example.createCriteria().andRelationIdEqualTo(request.getBelongId()).andRelationTypeEqualTo(request.getBelongType());
|
||||||
}
|
List<AttachmentModuleRelation> attachmentModuleRelations = attachmentModuleRelationMapper.selectByExample(example);
|
||||||
|
Map<String, String> relationMap = attachmentModuleRelations.stream()
|
||||||
|
.collect(Collectors.toMap(AttachmentModuleRelation::getAttachmentId,
|
||||||
|
relation -> relation.getFileMetadataRefId() == null ? "" : relation.getFileMetadataRefId()));
|
||||||
|
List<String> attachmentIds = attachmentModuleRelations.stream().map(AttachmentModuleRelation::getAttachmentId)
|
||||||
|
.filter(StringUtils::isNotEmpty)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (CollectionUtils.isNotEmpty(attachmentIds)) {
|
||||||
FileAttachmentMetadataExample fileExample = new FileAttachmentMetadataExample();
|
FileAttachmentMetadataExample fileExample = new FileAttachmentMetadataExample();
|
||||||
fileExample.createCriteria().andIdIn(attachmentIds);
|
fileExample.createCriteria().andIdIn(attachmentIds);
|
||||||
return fileAttachmentMetadataMapper.selectByExample(fileExample);
|
List<FileAttachmentMetadata> fileAttachmentMetadata = fileAttachmentMetadataMapper.selectByExample(fileExample);
|
||||||
|
fileAttachmentMetadata.forEach(file -> {
|
||||||
|
String fileRefId = relationMap.get(file.getId());
|
||||||
|
if (StringUtils.isEmpty(fileRefId)) {
|
||||||
|
file.setIsLocal(Boolean.TRUE);
|
||||||
|
file.setIsRelatedDeleted(Boolean.FALSE);
|
||||||
|
} else {
|
||||||
|
file.setIsLocal(Boolean.FALSE);
|
||||||
|
FileAssociation fileAssociation = fileAssociationMapper.selectByPrimaryKey(fileRefId);
|
||||||
|
if (fileAssociation != null) {
|
||||||
|
// 关联文件信息同步
|
||||||
|
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(fileAssociation.getFileMetadataId());
|
||||||
|
file.setIsRelatedDeleted(Boolean.FALSE);
|
||||||
|
file.setName(fileMetadata.getName());
|
||||||
|
file.setSize(fileMetadata.getSize());
|
||||||
|
file.setCreator(fileMetadata.getCreateUser());
|
||||||
|
file.setCreateTime(fileMetadata.getCreateTime());
|
||||||
|
} else {
|
||||||
|
file.setIsRelatedDeleted(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
attachments.addAll(fileAttachmentMetadata);
|
||||||
|
}
|
||||||
|
return attachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void relate(AttachmentRequest request) {
|
||||||
|
// 批量关联文件管理
|
||||||
|
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||||
|
FileAssociationMapper associationBatchMapper = sqlSession.getMapper(FileAssociationMapper.class);
|
||||||
|
AttachmentModuleRelationMapper attachmentModuleRelationBatchMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class);
|
||||||
|
FileAttachmentMetadataMapper fileAttachmentMetadataBatchMapper = sqlSession.getMapper(FileAttachmentMetadataMapper.class);
|
||||||
|
if (CollectionUtils.isNotEmpty(request.getMetadataRefIds())) {
|
||||||
|
request.getMetadataRefIds().forEach(metadataRefId -> {
|
||||||
|
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(metadataRefId);
|
||||||
|
FileAssociation fileAssociation = new FileAssociation();
|
||||||
|
fileAssociation.setId(UUID.randomUUID().toString());
|
||||||
|
fileAssociation.setFileMetadataId(metadataRefId);
|
||||||
|
fileAssociation.setFileType(fileMetadata.getType());
|
||||||
|
if (AttachmentType.TEST_CASE.type().equals(request.getBelongType())) {
|
||||||
|
fileAssociation.setType(FileAssociationType.TEST_CASE.name());
|
||||||
|
} else {
|
||||||
|
fileAssociation.setType(FileAssociationType.ISSUE.name());
|
||||||
|
}
|
||||||
|
fileAssociation.setProjectId(fileMetadata.getProjectId());
|
||||||
|
fileAssociation.setSourceItemId(metadataRefId);
|
||||||
|
fileAssociation.setSourceId(request.getBelongId());
|
||||||
|
associationBatchMapper.insert(fileAssociation);
|
||||||
|
AttachmentModuleRelation record = new AttachmentModuleRelation();
|
||||||
|
record.setRelationId(request.getBelongId());
|
||||||
|
record.setRelationType(request.getBelongType());
|
||||||
|
record.setFileMetadataRefId(fileAssociation.getId());
|
||||||
|
record.setAttachmentId(UUID.randomUUID().toString());
|
||||||
|
attachmentModuleRelationBatchMapper.insert(record);
|
||||||
|
FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
|
||||||
|
BeanUtils.copyBean(fileAttachmentMetadata, fileMetadata);
|
||||||
|
fileAttachmentMetadata.setId(record.getAttachmentId());
|
||||||
|
fileAttachmentMetadata.setCreator(fileMetadata.getCreateUser());
|
||||||
|
fileAttachmentMetadata.setFilePath(fileMetadata.getPath());
|
||||||
|
fileAttachmentMetadataBatchMapper.insert(fileAttachmentMetadata);
|
||||||
|
// 缺陷类型的附件, 关联时需单独同步第三方平台
|
||||||
|
if (AttachmentType.ISSUE.type().equals(request.getBelongType())) {
|
||||||
|
IssuesWithBLOBs issues = issuesMapper.selectByPrimaryKey(request.getBelongId());
|
||||||
|
IssuesUpdateRequest updateRequest = new IssuesUpdateRequest();
|
||||||
|
updateRequest.setPlatformId(issues.getPlatformId());
|
||||||
|
File refFile = downloadMetadataFile(metadataRefId, fileMetadata.getName());
|
||||||
|
IssuesRequest issuesRequest = new IssuesRequest();
|
||||||
|
issuesRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||||
|
Objects.requireNonNull(IssueFactory.createPlatform(issues.getPlatform(), issuesRequest))
|
||||||
|
.syncIssuesAttachment(updateRequest, refFile, AttachmentSyncType.UPLOAD);
|
||||||
|
FileUtils.deleteFile(FileUtils.ATTACHMENT_TMP_DIR + File.separator + fileMetadata.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sqlSession.flushStatements();
|
||||||
|
if (sqlSession != null && sqlSessionFactory != null) {
|
||||||
|
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unrelated(AttachmentRequest request) {
|
||||||
|
// 缺陷类型的附件, 取消关联时同步第三方平台
|
||||||
|
if (AttachmentType.ISSUE.type().equals(request.getBelongType())) {
|
||||||
|
IssuesWithBLOBs issues = issuesMapper.selectByPrimaryKey(request.getBelongId());
|
||||||
|
request.getMetadataRefIds().forEach(metadataRefId -> {
|
||||||
|
FileAttachmentMetadata fileAttachmentMetadata = fileAttachmentMetadataMapper.selectByPrimaryKey(metadataRefId);
|
||||||
|
IssuesUpdateRequest updateRequest = new IssuesUpdateRequest();
|
||||||
|
updateRequest.setPlatformId(issues.getPlatformId());
|
||||||
|
File deleteFile = new File(FileUtils.ATTACHMENT_TMP_DIR + File.separator + fileAttachmentMetadata.getName());
|
||||||
|
IssuesRequest issuesRequest = new IssuesRequest();
|
||||||
|
issuesRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||||
|
Objects.requireNonNull(IssueFactory.createPlatform(issues.getPlatform(), issuesRequest))
|
||||||
|
.syncIssuesAttachment(updateRequest, deleteFile, AttachmentSyncType.DELETE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
AttachmentModuleRelationExample example = new AttachmentModuleRelationExample();
|
||||||
|
example.createCriteria().andRelationIdEqualTo(request.getBelongId())
|
||||||
|
.andRelationTypeEqualTo(request.getBelongType())
|
||||||
|
.andAttachmentIdIn(request.getMetadataRefIds());
|
||||||
|
FileAttachmentMetadataExample exampleAttachment = new FileAttachmentMetadataExample();
|
||||||
|
exampleAttachment.createCriteria().andIdIn(request.getMetadataRefIds());
|
||||||
|
fileAttachmentMetadataMapper.deleteByExample(exampleAttachment);
|
||||||
|
attachmentModuleRelationMapper.deleteByExample(example);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAttachmentIdsByParam(AttachmentRequest request) {
|
public List<String> getAttachmentIdsByParam(AttachmentRequest request) {
|
||||||
|
@ -159,6 +311,15 @@ public class AttachmentService {
|
||||||
return attachmentIds;
|
return attachmentIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getRefIdByAttachmentId(String attachmentId) {
|
||||||
|
AttachmentModuleRelationExample example = new AttachmentModuleRelationExample();
|
||||||
|
example.createCriteria().andAttachmentIdEqualTo(attachmentId);
|
||||||
|
List<AttachmentModuleRelation> relations = attachmentModuleRelationMapper.selectByExample(example);
|
||||||
|
String associationId = relations.get(0).getFileMetadataRefId();
|
||||||
|
FileAssociation fileAssociation = fileAssociationMapper.selectByPrimaryKey(associationId);
|
||||||
|
return fileAssociation.getFileMetadataId();
|
||||||
|
}
|
||||||
|
|
||||||
public void initAttachment() {
|
public void initAttachment() {
|
||||||
List<AttachmentModuleRelation> attachmentModuleRelations = new ArrayList<>();
|
List<AttachmentModuleRelation> attachmentModuleRelations = new ArrayList<>();
|
||||||
List<IssueFile> issueFiles = issueFileMapper.selectByExample(new IssueFileExample());
|
List<IssueFile> issueFiles = issueFileMapper.selectByExample(new IssueFileExample());
|
||||||
|
@ -183,4 +344,9 @@ public class AttachmentService {
|
||||||
}
|
}
|
||||||
extAttachmentModuleRelationMapper.batchInsert(attachmentModuleRelations);
|
extAttachmentModuleRelationMapper.batchInsert(attachmentModuleRelations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File downloadMetadataFile(String fileMetadataRefId, String filename) {
|
||||||
|
byte[] refFileBytes = fileMetadataService.loadFileAsBytes(fileMetadataRefId);
|
||||||
|
return FileUtils.byteToFile(refFileBytes, FileUtils.ATTACHMENT_TMP_DIR, filename);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,10 @@ import io.metersphere.track.request.testcase.IssuesUpdateRequest;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.ibatis.session.ExecutorType;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
|
import org.mybatis.spring.SqlSessionUtils;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -44,6 +48,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
@ -91,11 +96,15 @@ public class IssuesService {
|
||||||
@Resource
|
@Resource
|
||||||
IssueFileMapper issueFileMapper;
|
IssueFileMapper issueFileMapper;
|
||||||
@Resource
|
@Resource
|
||||||
|
SqlSessionFactory sqlSessionFactory;
|
||||||
|
@Resource
|
||||||
private AttachmentService attachmentService;
|
private AttachmentService attachmentService;
|
||||||
@Resource
|
@Resource
|
||||||
private CustomFieldService customFieldService;
|
private CustomFieldService customFieldService;
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectMapper projectMapper;
|
private ProjectMapper projectMapper;
|
||||||
|
@Resource
|
||||||
|
private FileMetadataMapper fileMetadataMapper;
|
||||||
|
|
||||||
private static final String SYNC_THIRD_PARTY_ISSUES_KEY = "ISSUE:SYNC";
|
private static final String SYNC_THIRD_PARTY_ISSUES_KEY = "ISSUE:SYNC";
|
||||||
|
|
||||||
|
@ -129,9 +138,9 @@ public class IssuesService {
|
||||||
attachmentRequest.setBelongType(AttachmentType.ISSUE.type());
|
attachmentRequest.setBelongType(AttachmentType.ISSUE.type());
|
||||||
attachmentService.copyAttachment(attachmentRequest);
|
attachmentService.copyAttachment(attachmentRequest);
|
||||||
} else {
|
} else {
|
||||||
|
final String issueId = issues.getId();
|
||||||
// 新增, 需保存并同步所有待上传的附件
|
// 新增, 需保存并同步所有待上传的附件
|
||||||
if (CollectionUtils.isNotEmpty(files)) {
|
if (CollectionUtils.isNotEmpty(files)) {
|
||||||
final String issueId = issues.getId();
|
|
||||||
files.forEach(file -> {
|
files.forEach(file -> {
|
||||||
AttachmentRequest attachmentRequest = new AttachmentRequest();
|
AttachmentRequest attachmentRequest = new AttachmentRequest();
|
||||||
attachmentRequest.setBelongId(issueId);
|
attachmentRequest.setBelongId(issueId);
|
||||||
|
@ -139,6 +148,48 @@ public class IssuesService {
|
||||||
attachmentService.uploadAttachment(attachmentRequest, file);
|
attachmentService.uploadAttachment(attachmentRequest, file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 处理待关联的文件附件, 生成关联记录, 并同步至第三方平台
|
||||||
|
if (CollectionUtils.isNotEmpty(issuesRequest.getRelateFileMetaIds())) {
|
||||||
|
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||||
|
FileAssociationMapper associationBatchMapper = sqlSession.getMapper(FileAssociationMapper.class);
|
||||||
|
AttachmentModuleRelationMapper attachmentModuleRelationBatchMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class);
|
||||||
|
FileAttachmentMetadataMapper fileAttachmentMetadataBatchMapper = sqlSession.getMapper(FileAttachmentMetadataMapper.class);
|
||||||
|
issuesRequest.getRelateFileMetaIds().forEach(filemetaId -> {
|
||||||
|
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(filemetaId);
|
||||||
|
FileAssociation fileAssociation = new FileAssociation();
|
||||||
|
fileAssociation.setId(UUID.randomUUID().toString());
|
||||||
|
fileAssociation.setFileMetadataId(filemetaId);
|
||||||
|
fileAssociation.setFileType(fileMetadata.getType());
|
||||||
|
fileAssociation.setType(FileAssociationType.ISSUE.name());
|
||||||
|
fileAssociation.setProjectId(fileMetadata.getProjectId());
|
||||||
|
fileAssociation.setSourceItemId(filemetaId);
|
||||||
|
fileAssociation.setSourceId(issueId);
|
||||||
|
associationBatchMapper.insert(fileAssociation);
|
||||||
|
AttachmentModuleRelation relation = new AttachmentModuleRelation();
|
||||||
|
relation.setRelationId(issueId);
|
||||||
|
relation.setRelationType(AttachmentType.ISSUE.type());
|
||||||
|
relation.setFileMetadataRefId(fileAssociation.getId());
|
||||||
|
relation.setAttachmentId(UUID.randomUUID().toString());
|
||||||
|
attachmentModuleRelationBatchMapper.insert(relation);
|
||||||
|
FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
|
||||||
|
BeanUtils.copyBean(fileAttachmentMetadata, fileMetadata);
|
||||||
|
fileAttachmentMetadata.setId(relation.getAttachmentId());
|
||||||
|
fileAttachmentMetadata.setCreator(fileMetadata.getCreateUser());
|
||||||
|
fileAttachmentMetadata.setFilePath(fileMetadata.getPath());
|
||||||
|
fileAttachmentMetadataBatchMapper.insert(fileAttachmentMetadata);
|
||||||
|
// 下载文件管理文件, 同步到第三方平台
|
||||||
|
File refFile = attachmentService.downloadMetadataFile(filemetaId, fileMetadata.getName());
|
||||||
|
IssuesRequest addIssueRequest = new IssuesRequest();
|
||||||
|
addIssueRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||||
|
Objects.requireNonNull(IssueFactory.createPlatform(issuesRequest.getPlatform(), addIssueRequest))
|
||||||
|
.syncIssuesAttachment(issuesRequest, refFile, AttachmentSyncType.UPLOAD);
|
||||||
|
FileUtils.deleteFile(FileUtils.ATTACHMENT_TMP_DIR + File.separator + fileMetadata.getName());
|
||||||
|
});
|
||||||
|
sqlSession.flushStatements();
|
||||||
|
if (sqlSession != null && sqlSessionFactory != null) {
|
||||||
|
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return getIssue(issues.getId());
|
return getIssue(issues.getId());
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import io.metersphere.api.service.ApiTestCaseService;
|
||||||
import io.metersphere.base.domain.*;
|
import io.metersphere.base.domain.*;
|
||||||
import io.metersphere.base.domain.ext.CustomFieldResource;
|
import io.metersphere.base.domain.ext.CustomFieldResource;
|
||||||
import io.metersphere.base.mapper.*;
|
import io.metersphere.base.mapper.*;
|
||||||
|
import io.metersphere.base.mapper.ext.ExtAttachmentModuleRelationMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
|
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtProjectVersionMapper;
|
import io.metersphere.base.mapper.ext.ExtProjectVersionMapper;
|
||||||
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
||||||
|
@ -135,6 +136,8 @@ public class TestCaseService {
|
||||||
@Resource
|
@Resource
|
||||||
AttachmentModuleRelationMapper attachmentModuleRelationMapper;
|
AttachmentModuleRelationMapper attachmentModuleRelationMapper;
|
||||||
@Resource
|
@Resource
|
||||||
|
ExtAttachmentModuleRelationMapper extAttachmentModuleRelationMapper;
|
||||||
|
@Resource
|
||||||
private LoadTestMapper loadTestMapper;
|
private LoadTestMapper loadTestMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ApiScenarioMapper apiScenarioMapper;
|
private ApiScenarioMapper apiScenarioMapper;
|
||||||
|
@ -2101,6 +2104,41 @@ public class TestCaseService {
|
||||||
attachmentService.uploadAttachment(attachmentRequest, file);
|
attachmentService.uploadAttachment(attachmentRequest, file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// 同步待关联的文件附件, 生成关联记录
|
||||||
|
if (CollectionUtils.isNotEmpty(request.getRelateFileMetaIds())) {
|
||||||
|
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||||
|
FileAssociationMapper associationBatchMapper = sqlSession.getMapper(FileAssociationMapper.class);
|
||||||
|
AttachmentModuleRelationMapper attachmentModuleRelationBatchMapper = sqlSession.getMapper(AttachmentModuleRelationMapper.class);
|
||||||
|
FileAttachmentMetadataMapper fileAttachmentMetadataBatchMapper = sqlSession.getMapper(FileAttachmentMetadataMapper.class);
|
||||||
|
request.getRelateFileMetaIds().forEach(filemetaId -> {
|
||||||
|
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(filemetaId);
|
||||||
|
FileAssociation fileAssociation = new FileAssociation();
|
||||||
|
fileAssociation.setId(UUID.randomUUID().toString());
|
||||||
|
fileAssociation.setFileMetadataId(filemetaId);
|
||||||
|
fileAssociation.setFileType(fileMetadata.getType());
|
||||||
|
fileAssociation.setType(FileAssociationType.TEST_CASE.name());
|
||||||
|
fileAssociation.setProjectId(fileMetadata.getProjectId());
|
||||||
|
fileAssociation.setSourceItemId(filemetaId);
|
||||||
|
fileAssociation.setSourceId(testCaseWithBLOBs.getId());
|
||||||
|
associationBatchMapper.insert(fileAssociation);
|
||||||
|
AttachmentModuleRelation record = new AttachmentModuleRelation();
|
||||||
|
record.setRelationId(testCaseWithBLOBs.getId());
|
||||||
|
record.setRelationType(AttachmentType.TEST_CASE.type());
|
||||||
|
record.setFileMetadataRefId(fileAssociation.getId());
|
||||||
|
record.setAttachmentId(UUID.randomUUID().toString());
|
||||||
|
attachmentModuleRelationBatchMapper.insert(record);
|
||||||
|
FileAttachmentMetadata fileAttachmentMetadata = new FileAttachmentMetadata();
|
||||||
|
BeanUtils.copyBean(fileAttachmentMetadata, fileMetadata);
|
||||||
|
fileAttachmentMetadata.setId(record.getAttachmentId());
|
||||||
|
fileAttachmentMetadata.setCreator(fileMetadata.getCreateUser());
|
||||||
|
fileAttachmentMetadata.setFilePath(fileMetadata.getPath());
|
||||||
|
fileAttachmentMetadataBatchMapper.insert(fileAttachmentMetadata);
|
||||||
|
});
|
||||||
|
sqlSession.flushStatements();
|
||||||
|
if (sqlSession != null && sqlSessionFactory != null) {
|
||||||
|
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return testCaseWithBLOBs;
|
return testCaseWithBLOBs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
package io.metersphere.xmind.utils;
|
package io.metersphere.xmind.utils;
|
||||||
|
|
||||||
|
import io.metersphere.commons.utils.FileUtils;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
import org.apache.commons.fileupload.disk.DiskFileItem;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
import org.springframework.web.multipart.commons.CommonsMultipartFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工具类
|
* 工具类
|
||||||
|
@ -47,6 +48,23 @@ public class FileUtil {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File 转 MultipartFile
|
||||||
|
* @param file
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static MultipartFile fileToMultipartFile(File file) {
|
||||||
|
DiskFileItem item = new DiskFileItem("file", MediaType.MULTIPART_FORM_DATA_VALUE, true,
|
||||||
|
file.getName(), (int)file.length(), file.getParentFile());
|
||||||
|
try {
|
||||||
|
OutputStream os = item.getOutputStream();
|
||||||
|
os.write(FileUtils.fileToByte(file));
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new CommonsMultipartFile(item);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean deleteDir(File dir) {
|
public static boolean deleteDir(File dir) {
|
||||||
if (dir.isDirectory()) {
|
if (dir.isDirectory()) {
|
||||||
String[] children = dir.list();
|
String[] children = dir.list();
|
||||||
|
|
|
@ -151,3 +151,8 @@ ALTER TABLE `file_metadata` ADD INDEX `INDEX_REF_ID`(`ref_id`);
|
||||||
ALTER TABLE `ui_scenario` ADD COLUMN scenario_type VARCHAR(100) NOT NULL DEFAULT 'scenario' COMMENT 'Scenario type' AFTER `level`;
|
ALTER TABLE `ui_scenario` ADD COLUMN scenario_type VARCHAR(100) NOT NULL DEFAULT 'scenario' COMMENT 'Scenario type' AFTER `level`;
|
||||||
|
|
||||||
ALTER TABLE `ui_scenario_module` ADD COLUMN scenario_type VARCHAR(100) NOT NULL DEFAULT 'scenario' COMMENT 'Scenario type' AFTER `level`;
|
ALTER TABLE `ui_scenario_module` ADD COLUMN scenario_type VARCHAR(100) NOT NULL DEFAULT 'scenario' COMMENT 'Scenario type' AFTER `level`;
|
||||||
|
|
||||||
|
-- 功能用例, 缺陷管理文件管理关联字段
|
||||||
|
ALTER TABLE `attachment_module_relation` MODIFY COLUMN attachment_id VARCHAR(50) NULL;
|
||||||
|
|
||||||
|
ALTER TABLE `attachment_module_relation` ADD COLUMN file_metadata_ref_id VARCHAR(50) DEFAULT NULL COMMENT 'FILE ASSOCIATION ID';
|
||||||
|
|
|
@ -2,13 +2,22 @@
|
||||||
<div>
|
<div>
|
||||||
<el-row type="flex" justify="center">
|
<el-row type="flex" justify="center">
|
||||||
<el-col>
|
<el-col>
|
||||||
<el-table class="basic-config" :data="tableData">
|
<el-table class="basic-config" :data="tableData" :row-class-name="handleIsRelated">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="name"
|
prop="name"
|
||||||
:label="$t('load_test.file_name')">
|
:label="$t('load_test.file_name')">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<el-tooltip class="item" effect="dark" :content="scope.row.name" placement="top">
|
<el-tooltip class="item" effect="dark" :content="scope.row.name" placement="top">
|
||||||
<el-progress
|
<el-progress
|
||||||
|
class="row-delete-name"
|
||||||
|
type="line"
|
||||||
|
v-if="!scope.row.isLocal && scope.row.isRelatedDeleted"
|
||||||
|
:stroke-width="40"
|
||||||
|
:text-inside="true"
|
||||||
|
:format="clearPercentage(scope.row)">
|
||||||
|
</el-progress>
|
||||||
|
<el-progress
|
||||||
|
v-else
|
||||||
:color="scope.row.progress >= 100 ? '' : uploadProgressColor"
|
:color="scope.row.progress >= 100 ? '' : uploadProgressColor"
|
||||||
type="line"
|
type="line"
|
||||||
:format="clearPercentage(scope.row)"
|
:format="clearPercentage(scope.row)"
|
||||||
|
@ -37,7 +46,7 @@
|
||||||
:width="70"
|
:width="70"
|
||||||
:label="$t('commons.status')">
|
:label="$t('commons.status')">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<span :class="scope.row.status === 'success' ? 'green' : scope.row.status === 'error' ? 'red' : scope.row.status === 'toUpload' ? 'yellow' : ''">{{ scope.row.status | formatStatus}}</span>
|
<span :class="scope.row.status === 'expired' ? 'lightgrey' : scope.row.status === 'success' ? 'green' : scope.row.status === 'error' ? 'red' : scope.row.status === 'toUpload' || 'toRelate' ? 'yellow' : ''">{{ formatStatus(scope.row.status) }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
|
@ -46,19 +55,27 @@
|
||||||
:label="$t('group.operator')">
|
:label="$t('group.operator')">
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:width="140"
|
:width="180"
|
||||||
:label="$t('commons.operating')">
|
:label="$t('commons.operating')">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
<el-button @click="preview(scope.row)" :disabled="!scope.row.id" type="primary"
|
<el-button @click="preview(scope.row)" type="primary" :disabled="!scope.row.id || scope.row.status === 'toRelate' || scope.row.isRelatedDeleted"
|
||||||
v-if="scope.row.progress === 100 && isPreview(scope.row)"
|
v-if="scope.row.progress === 100 && isPreview(scope.row)"
|
||||||
icon="el-icon-view" size="mini" circle/>
|
icon="el-icon-view" size="mini" circle/>
|
||||||
<el-button @click="handleDownload(scope.row)" type="primary" :disabled="!scope.row.id"
|
<el-button @click="handleDownload(scope.row)" type="primary" :disabled="!scope.row.id || scope.row.status === 'toRelate' || scope.row.isRelatedDeleted"
|
||||||
v-if="scope.row.progress === 100"
|
v-if="scope.row.progress === 100"
|
||||||
icon="el-icon-download" size="mini" circle/>
|
icon="el-icon-download" size="mini" circle/>
|
||||||
|
<el-button :disabled="isCopy || !scope.row.id"
|
||||||
|
@click="handleUpload(scope.row)" type="primary"
|
||||||
|
v-if="scope.row.progress === 100 && scope.row.isLocal"
|
||||||
|
icon="el-icon-upload" size="mini" circle/>
|
||||||
<el-button :disabled="readOnly || !isDelete || isCopy || (!scope.row.id && scope.row.status !== 'toUpload')"
|
<el-button :disabled="readOnly || !isDelete || isCopy || (!scope.row.id && scope.row.status !== 'toUpload')"
|
||||||
@click="handleDelete(scope.row, scope.$index)" type="danger"
|
@click="handleDelete(scope.row, scope.$index)" type="danger"
|
||||||
v-if="scope.row.progress === 100"
|
v-if="scope.row.progress === 100 && scope.row.isLocal"
|
||||||
icon="el-icon-delete" size="mini"
|
icon="el-icon-delete" size="mini" circle/>
|
||||||
|
<el-button :disabled="readOnly || !isDelete || isCopy || (!scope.row.id && scope.row.status !== 'toRelate')"
|
||||||
|
@click="handleUnRelate(scope.row, scope.$index)" type="danger"
|
||||||
|
v-if="scope.row.progress === 100 && !scope.row.isLocal"
|
||||||
|
icon="el-icon-unlock" size="mini"
|
||||||
circle/>
|
circle/>
|
||||||
<el-button :disabled="readOnly || !isDelete" @click="handleCancel(scope.row, scope.$index)" type="danger"
|
<el-button :disabled="readOnly || !isDelete" @click="handleCancel(scope.row, scope.$index)" type="danger"
|
||||||
v-if="scope.row.progress < 100"
|
v-if="scope.row.progress < 100"
|
||||||
|
@ -100,10 +117,14 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
uploadProgressColor: '#d4f6d4',
|
uploadProgressColor: '#d4f6d4',
|
||||||
uploadSuccessColor: '#FFFFFF'
|
uploadSuccessColor: '#FFFFFF',
|
||||||
|
that: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
handleIsRelated(row, rowIndex) {
|
||||||
|
return row.row.isRelatedDeleted ? 'delete-row' : '';
|
||||||
|
},
|
||||||
clearPercentage(row) {
|
clearPercentage(row) {
|
||||||
return () => {
|
return () => {
|
||||||
return row.name;
|
return row.name;
|
||||||
|
@ -120,19 +141,35 @@ export default {
|
||||||
this.$fileDownloadPost('/attachment/download', {
|
this.$fileDownloadPost('/attachment/download', {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
id: file.id,
|
id: file.id,
|
||||||
|
isLocal: file.isLocal
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
handleUpload(file) {
|
||||||
|
this.$emit("handleDump", file);
|
||||||
|
},
|
||||||
handleDelete(file, index) {
|
handleDelete(file, index) {
|
||||||
this.$emit("handleDelete", file, index);
|
this.$emit("handleDelete", file, index);
|
||||||
},
|
},
|
||||||
|
handleUnRelate(file, index) {
|
||||||
|
this.$emit("handleUnRelate", file, index);
|
||||||
|
},
|
||||||
handleCancel(file, index) {
|
handleCancel(file, index) {
|
||||||
this.$emit("handleCancel", file, index);
|
this.$emit("handleCancel", file, index);
|
||||||
},
|
},
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
formatStatus(status) {
|
formatStatus(status) {
|
||||||
if (isNaN(status)) {
|
if (isNaN(status)) {
|
||||||
return status === 'success' ? '完成' : status === 'toUpload' ? '待上传' : '失败'
|
switch (status) {
|
||||||
|
case 'success':
|
||||||
|
return this.$t('commons.file_upload_status.success');
|
||||||
|
case 'toUpload':
|
||||||
|
return this.$t('commons.file_upload_status.to_upload');
|
||||||
|
case 'toRelate':
|
||||||
|
return this.$t('commons.file_upload_status.to_relate');
|
||||||
|
case 'expired':
|
||||||
|
return this.$t('commons.file_upload_status.expired');
|
||||||
|
default:
|
||||||
|
return this.$t('commons.file_upload_status.error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Math.floor(status * 100 / 100) + "%";
|
return Math.floor(status * 100 / 100) + "%";
|
||||||
}
|
}
|
||||||
|
@ -145,6 +182,10 @@ export default {
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::v-deep .el-progress.row-delete-name .el-progress-bar__innerText {
|
||||||
|
color: lightgrey!important;
|
||||||
|
}
|
||||||
|
|
||||||
::v-deep .el-progress-bar__outer,
|
::v-deep .el-progress-bar__outer,
|
||||||
::v-deep .el-progress-bar__inner {
|
::v-deep .el-progress-bar__inner {
|
||||||
border-radius: inherit ;
|
border-radius: inherit ;
|
||||||
|
@ -169,4 +210,14 @@ export default {
|
||||||
.yellow {
|
.yellow {
|
||||||
color: #E6A23C;
|
color: #E6A23C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lightgrey {
|
||||||
|
color: lightgrey;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.el-table .delete-row {
|
||||||
|
color: lightgrey;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -742,6 +742,9 @@ export default {
|
||||||
if (this.validate(param)) {
|
if (this.validate(param)) {
|
||||||
let option = this.getOption(param);
|
let option = this.getOption(param);
|
||||||
this.result = this.$request(option, (response) => {
|
this.result = this.$request(option, (response) => {
|
||||||
|
// 保存用例后刷新附件
|
||||||
|
this.currentTestCaseInfo.isCopy = false;
|
||||||
|
this.$refs.otherInfo.getFileMetaData(response.data.id);
|
||||||
this.$success(this.$t('commons.save_success'));
|
this.$success(this.$t('commons.save_success'));
|
||||||
this.path = "/test/case/edit";
|
this.path = "/test/case/edit";
|
||||||
// this.operationType = "edit"
|
// this.operationType = "edit"
|
||||||
|
@ -767,8 +770,6 @@ export default {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(this);
|
callback(this);
|
||||||
}
|
}
|
||||||
// 保存用例后刷新附件
|
|
||||||
|
|
||||||
//更新版本
|
//更新版本
|
||||||
if (hasLicense()) {
|
if (hasLicense()) {
|
||||||
this.getVersionHistory();
|
this.getVersionHistory();
|
||||||
|
@ -810,6 +811,12 @@ export default {
|
||||||
if (this.selectedOtherInfo) {
|
if (this.selectedOtherInfo) {
|
||||||
param.otherInfoConfig = this.selectedOtherInfo;
|
param.otherInfoConfig = this.selectedOtherInfo;
|
||||||
}
|
}
|
||||||
|
if (this.$refs.otherInfo.relateFiles.length > 0) {
|
||||||
|
param.relateFileMetaIds = this.$refs.otherInfo.relateFiles;
|
||||||
|
}
|
||||||
|
if (this.$refs.otherInfo.unRelateFiles.length > 0) {
|
||||||
|
param.unRelateFileMetaIds = this.$refs.otherInfo.unRelateFiles;
|
||||||
|
}
|
||||||
return param;
|
return param;
|
||||||
},
|
},
|
||||||
parseOldFields(param) {
|
parseOldFields(param) {
|
||||||
|
|
|
@ -60,7 +60,10 @@
|
||||||
|
|
||||||
<el-tab-pane :label="$t('test_track.case.attachment')" name="attachment">
|
<el-tab-pane :label="$t('test_track.case.attachment')" name="attachment">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="22">
|
<el-col :span="22" style="margin-bottom: 10px;">
|
||||||
|
<div class="upload-default" @click.stop>
|
||||||
|
<el-popover placement="right" trigger="hover">
|
||||||
|
<div>
|
||||||
<el-upload
|
<el-upload
|
||||||
multiple
|
multiple
|
||||||
:limit="8"
|
:limit="8"
|
||||||
|
@ -74,9 +77,16 @@
|
||||||
:on-success="handleSuccess"
|
:on-success="handleSuccess"
|
||||||
:on-error="handleError"
|
:on-error="handleError"
|
||||||
:disabled="readOnly || isCopy">
|
:disabled="readOnly || isCopy">
|
||||||
<el-button :disabled="readOnly || isCopy" type="primary" size="mini">{{$t('test_track.case.add_attachment')}}</el-button>
|
<el-button :disabled="readOnly || isCopy" type="text">{{$t('permission.project_file.local_upload')}}</el-button>
|
||||||
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
|
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
<el-button type="text" :disabled="readOnly || isCopy" @click="associationFile">{{ $t('permission.project_file.associated_files') }}</el-button>
|
||||||
|
<i class="el-icon-plus" slot="reference"/>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
<div :class="readOnly ? 'testplan-local-upload-tip' : 'not-testplan-local-upload-tip'">
|
||||||
|
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
|
||||||
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
|
@ -86,6 +96,8 @@
|
||||||
:is-copy="isCopy"
|
:is-copy="isCopy"
|
||||||
:is-delete="!isTestPlan"
|
:is-delete="!isTestPlan"
|
||||||
@handleDelete="handleDelete"
|
@handleDelete="handleDelete"
|
||||||
|
@handleUnRelate="handleUnRelate"
|
||||||
|
@handleDump="handleDump"
|
||||||
@handleCancel="handleCancel"/>
|
@handleCancel="handleCancel"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
@ -121,6 +133,8 @@
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
<ms-file-metadata-list ref="metadataList" @checkRows="checkRows"/>
|
||||||
|
<ms-file-batch-move ref="module" @setModuleId="setModuleId"/>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -137,10 +151,12 @@ import TabPaneCount from "@/business/components/track/plan/view/comonents/report
|
||||||
import {getRelationshipCountCase} from "@/network/testCase";
|
import {getRelationshipCountCase} from "@/network/testCase";
|
||||||
import TestCaseComment from "@/business/components/track/case/components/TestCaseComment";
|
import TestCaseComment from "@/business/components/track/case/components/TestCaseComment";
|
||||||
import ReviewCommentItem from "@/business/components/track/review/commom/ReviewCommentItem";
|
import ReviewCommentItem from "@/business/components/track/review/commom/ReviewCommentItem";
|
||||||
import {byteToSize, getTypeByFileName, hasLicense} from "@/common/js/utils";
|
import {byteToSize, getCurrentProjectID, getTypeByFileName, getUUID, hasLicense} from "@/common/js/utils";
|
||||||
import {TokenKey} from "@/common/js/constants";
|
import {TokenKey} from "@/common/js/constants";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {validateAndSetLicense} from "@/business/permission";
|
import {validateAndSetLicense} from "@/business/permission";
|
||||||
|
import MsFileMetadataList from "@/business/components/project/menu/file/quote/QuoteFileList";
|
||||||
|
import MsFileBatchMove from "@/business/components/project/menu/file/module/FileBatchMove";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TestCaseEditOtherInfo",
|
name: "TestCaseEditOtherInfo",
|
||||||
|
@ -150,6 +166,8 @@ export default {
|
||||||
TestCaseTestRelate,
|
TestCaseTestRelate,
|
||||||
TestCaseComment,
|
TestCaseComment,
|
||||||
ReviewCommentItem,
|
ReviewCommentItem,
|
||||||
|
MsFileMetadataList,
|
||||||
|
MsFileBatchMove,
|
||||||
FormRichTextItem, TestCaseIssueRelate, TestCaseAttachment, MsRichText, TestCaseRichText
|
FormRichTextItem, TestCaseIssueRelate, TestCaseAttachment, MsRichText, TestCaseRichText
|
||||||
},
|
},
|
||||||
props: ['form', 'labelWidth', 'caseId', 'readOnly', 'projectId', 'isTestPlan', 'planId', 'versionEnable', 'isCopy', 'copyCaseId',
|
props: ['form', 'labelWidth', 'caseId', 'readOnly', 'projectId', 'isTestPlan', 'planId', 'versionEnable', 'isCopy', 'copyCaseId',
|
||||||
|
@ -174,7 +192,10 @@ export default {
|
||||||
},
|
},
|
||||||
intervalMap: new Map(),
|
intervalMap: new Map(),
|
||||||
cancelFileToken: [],
|
cancelFileToken: [],
|
||||||
uploadFiles: []
|
uploadFiles: [],
|
||||||
|
relateFiles: [],
|
||||||
|
unRelateFiles: [],
|
||||||
|
dumpFile: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -262,7 +283,8 @@ export default {
|
||||||
progress: this.type === 'add' ? 100 : 0,
|
progress: this.type === 'add' ? 100 : 0,
|
||||||
status: this.type === 'add' ? 'toUpload' : 0,
|
status: this.type === 'add' ? 'toUpload' : 0,
|
||||||
creator: user.name,
|
creator: user.name,
|
||||||
type: getTypeByFileName(file.name)
|
type: getTypeByFileName(file.name),
|
||||||
|
isLocal: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.type === 'add') {
|
if (this.type === 'add') {
|
||||||
|
@ -365,7 +387,8 @@ export default {
|
||||||
this.fileList.splice(index, 1);
|
this.fileList.splice(index, 1);
|
||||||
this.tableData.splice(index, 1);
|
this.tableData.splice(index, 1);
|
||||||
if (this.type === 'add') {
|
if (this.type === 'add') {
|
||||||
this.uploadFiles.splice(index, 1);
|
let delIndex = this.uploadFiles.findIndex(uploadFile => uploadFile.name === file.name)
|
||||||
|
this.uploadFiles.splice(delIndex, 1);
|
||||||
} else {
|
} else {
|
||||||
this.$get('/attachment/delete/testcase/' + file.id , response => {
|
this.$get('/attachment/delete/testcase/' + file.id , response => {
|
||||||
this.$success(this.$t('commons.delete_success'));
|
this.$success(this.$t('commons.delete_success'));
|
||||||
|
@ -373,6 +396,36 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleUnRelate(file, index) {
|
||||||
|
// 取消关联
|
||||||
|
this.$alert(this.$t('load_test.unrelated_file_confirm') + file.name + "?", '', {
|
||||||
|
confirmButtonText: this.$t('commons.confirm'),
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
callback: (action) => {
|
||||||
|
if (action === 'confirm') {
|
||||||
|
let unRelateFileIndex = this.tableData.findIndex(f => f.name === file.name);
|
||||||
|
this.tableData.splice(unRelateFileIndex, 1);
|
||||||
|
if (file.status === 'toRelate') {
|
||||||
|
// 待关联的记录, 直接移除
|
||||||
|
let unRelateId = this.relateFiles.findIndex(f => f === file.id);
|
||||||
|
this.relateFiles.splice(unRelateId, 1);
|
||||||
|
} else {
|
||||||
|
// 已经关联的记录
|
||||||
|
this.unRelateFiles.push(file.id);
|
||||||
|
let data = {'belongType': 'testcase', 'belongId': this.caseId, 'metadataRefIds': this.unRelateFiles};
|
||||||
|
this.$post('/attachment/metadata/unrelated', data, response => {
|
||||||
|
this.$success(this.$t('commons.unrelated_success'));
|
||||||
|
this.getFileMetaData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleDump(file) {
|
||||||
|
this.$refs.module.init();
|
||||||
|
this.dumpFile = file;
|
||||||
|
},
|
||||||
handleCancel(file, index) {
|
handleCancel(file, index) {
|
||||||
this.fileList.splice(index, 1);
|
this.fileList.splice(index, 1);
|
||||||
let cancelToken = this.cancelFileToken.filter(f => f.name === file.name)[0];
|
let cancelToken = this.cancelFileToken.filter(f => f.name === file.name)[0];
|
||||||
|
@ -382,13 +435,17 @@ export default {
|
||||||
cancelFile.status = 'error';
|
cancelFile.status = 'error';
|
||||||
},
|
},
|
||||||
getFileMetaData(id) {
|
getFileMetaData(id) {
|
||||||
|
if (this.type === 'edit') {
|
||||||
|
this.relateFiles = [];
|
||||||
|
this.unRelateFiles = [];
|
||||||
|
}
|
||||||
this.$emit("update:isClickAttachmentTab", true);
|
this.$emit("update:isClickAttachmentTab", true);
|
||||||
// 保存用例后传入用例id,刷新文件列表,可以预览和下载
|
// 保存用例后传入用例id,刷新文件列表,可以预览和下载
|
||||||
this.fileList = [];
|
this.fileList = [];
|
||||||
this.tableData = [];
|
this.tableData = [];
|
||||||
let testCaseId;
|
let testCaseId;
|
||||||
if (this.isCopy) {
|
if (this.isCopy) {
|
||||||
testCaseId = this.copyCaseId
|
testCaseId = id ? id : this.copyCaseId
|
||||||
} else {
|
} else {
|
||||||
testCaseId = id ? id : this.caseId;
|
testCaseId = id ? id : this.caseId;
|
||||||
}
|
}
|
||||||
|
@ -404,12 +461,61 @@ export default {
|
||||||
this.tableData = JSON.parse(JSON.stringify(files));
|
this.tableData = JSON.parse(JSON.stringify(files));
|
||||||
this.tableData.map(f => {
|
this.tableData.map(f => {
|
||||||
f.size = byteToSize(f.size);
|
f.size = byteToSize(f.size);
|
||||||
f.status = 'success';
|
f.status = f.isRelatedDeleted ? 'expired' : 'success';
|
||||||
f.progress = 100
|
f.progress = 100
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
associationFile() {
|
||||||
|
this.$refs.metadataList.open();
|
||||||
|
},
|
||||||
|
checkRows(rows) {
|
||||||
|
let repeatRecord = false;
|
||||||
|
for (let row of rows) {
|
||||||
|
let rowIndex = this.tableData.findIndex(item => item.name === row.name);
|
||||||
|
if (rowIndex >= 0) {
|
||||||
|
this.$error(this.$t('load_test.exist_related_file') + ": " + row.name);
|
||||||
|
repeatRecord = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!repeatRecord) {
|
||||||
|
if (this.type === 'add') {
|
||||||
|
// 新增
|
||||||
|
rows.forEach(row => {
|
||||||
|
this.relateFiles.push(row.id);
|
||||||
|
this.tableData.push({
|
||||||
|
id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
size: byteToSize(row.size),
|
||||||
|
updateTime: row.createTime,
|
||||||
|
progress: 100,
|
||||||
|
status: 'toRelate',
|
||||||
|
creator: row.createUser,
|
||||||
|
type: row.type,
|
||||||
|
isLocal: false,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 编辑
|
||||||
|
let metadataRefIds = [];
|
||||||
|
rows.forEach(row => metadataRefIds.push(row.id));
|
||||||
|
let data = {'belongType': 'testcase', 'belongId': this.caseId, 'metadataRefIds': metadataRefIds};
|
||||||
|
this.$post('/attachment/metadata/relate', data, response => {
|
||||||
|
this.$success(this.$t('commons.relate_success'));
|
||||||
|
this.getFileMetaData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setModuleId(moduleId) {
|
||||||
|
let data = {id: getUUID(), resourceId: getCurrentProjectID(), moduleId: moduleId,
|
||||||
|
projectId: getCurrentProjectID(), fileName: this.dumpFile.name, attachmentId: this.dumpFile.id};
|
||||||
|
this.$post("/attachment/metadata/dump", data, (response) => {
|
||||||
|
this.$success(this.$t("organization.integration.successful_operation"));
|
||||||
|
});
|
||||||
|
},
|
||||||
getRelatedTest() {
|
getRelatedTest() {
|
||||||
this.$refs.relateTest.initTable();
|
this.$refs.relateTest.initTable();
|
||||||
},
|
},
|
||||||
|
@ -510,4 +616,45 @@ export default {
|
||||||
.demandInput {
|
.demandInput {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-icon-plus {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-default {
|
||||||
|
background-color: #fbfdff;
|
||||||
|
border: 1px dashed #c0ccda;
|
||||||
|
border-radius: 6px;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 40px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 32px;
|
||||||
|
vertical-align: top;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-default i {
|
||||||
|
color: #8c939d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-default:hover {
|
||||||
|
border: 1px dashed #783887;
|
||||||
|
}
|
||||||
|
|
||||||
|
.testplan-local-upload-tip {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
left: 25px;
|
||||||
|
top: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-testplan-local-upload-tip {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
left: 25px;
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<el-dialog :visible.sync="dialogVisible" width="80%" :destroy-on-close="true" :before-close="close" :append-to-body="true">
|
<el-dialog :visible.sync="dialogVisible" width="80%" :destroy-on-close="true" :before-close="close" :append-to-body="true">
|
||||||
<div>
|
<div>
|
||||||
<img :src="'/attachment/preview/' + file.id" :alt="$t('test_track.case.img_loading_fail')" style="width: 100%;height: 100%;"
|
<img :src="'/attachment/preview/' + file.id + '/' + file.isLocal" :alt="$t('test_track.case.img_loading_fail')" style="width: 100%;height: 100%;"
|
||||||
v-if="file.type === 'JPG' || file.type === 'JPEG' || file.type === 'PNG'">
|
v-if="file.type === 'JPG' || file.type === 'JPEG' || file.type === 'PNG'">
|
||||||
<div v-if="file.type === 'PDF'">
|
<div v-if="file.type === 'PDF'">
|
||||||
<test-case-pdf :file-id="file.id"/>
|
<test-case-pdf :file-id="file.id" :is-local="file.isLocal"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
|
@ -10,7 +10,8 @@ export default {
|
||||||
name: "TestCasePdf",
|
name: "TestCasePdf",
|
||||||
components: {pdf},
|
components: {pdf},
|
||||||
props: {
|
props: {
|
||||||
fileId: String
|
fileId: String,
|
||||||
|
isLocal: Boolean
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -21,7 +22,7 @@ export default {
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.loadingTask = pdf.createLoadingTask("/attachment/preview/" + this.fileId);
|
this.loadingTask = pdf.createLoadingTask("/attachment/preview/" + this.fileId + "/" + this.isLocal);
|
||||||
this.loadingTask.promise.then(pdf => {
|
this.loadingTask.promise.then(pdf => {
|
||||||
this.numPages = pdf.numPages
|
this.numPages = pdf.numPages
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
|
@ -102,7 +102,10 @@
|
||||||
|
|
||||||
<el-tab-pane :label="$t('test_track.case.attachment')" name="attachment">
|
<el-tab-pane :label="$t('test_track.case.attachment')" name="attachment">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="22">
|
<el-col :span="22" style="margin-bottom: 10px;">
|
||||||
|
<div class="upload-default" @click.stop>
|
||||||
|
<el-popover placement="right" trigger="hover">
|
||||||
|
<div>
|
||||||
<el-upload
|
<el-upload
|
||||||
multiple
|
multiple
|
||||||
:limit="8"
|
:limit="8"
|
||||||
|
@ -115,10 +118,17 @@
|
||||||
:on-exceed="handleExceed"
|
:on-exceed="handleExceed"
|
||||||
:on-success="handleSuccess"
|
:on-success="handleSuccess"
|
||||||
:on-error="handleError"
|
:on-error="handleError"
|
||||||
:disabled="type === 'copy'">
|
:disabled="readOnly || type === 'copy'">
|
||||||
<el-button type="primary" :disabled="type === 'copy'" size="mini">{{$t('test_track.case.add_attachment')}}</el-button>
|
<el-button :disabled="readOnly || type === 'copy'" type="text">{{$t('permission.project_file.local_upload')}}</el-button>
|
||||||
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
|
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
<el-button type="text" :disabled="readOnly || type === 'copy'" @click="associationFile">{{ $t('permission.project_file.associated_files') }}</el-button>
|
||||||
|
<i class="el-icon-plus" slot="reference"/>
|
||||||
|
</el-popover>
|
||||||
|
</div>
|
||||||
|
<div class="local-upload-tips">
|
||||||
|
<span slot="tip" class="el-upload__tip"> {{ $t('test_track.case.upload_tip') }} </span>
|
||||||
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row style="margin-top: 10px">
|
<el-row style="margin-top: 10px">
|
||||||
|
@ -128,7 +138,9 @@
|
||||||
:is-delete="isDelete"
|
:is-delete="isDelete"
|
||||||
:is-copy="type === 'copy'"
|
:is-copy="type === 'copy'"
|
||||||
@handleDelete="handleDelete"
|
@handleDelete="handleDelete"
|
||||||
@handleCancel="handleCancel"/>
|
@handleCancel="handleCancel"
|
||||||
|
@handleUnRelate="handleUnRelate"
|
||||||
|
@handleDump="handleDump"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
@ -170,9 +182,10 @@
|
||||||
<issue-comment :issues-id="form.id"
|
<issue-comment :issues-id="form.id"
|
||||||
@getComments="getComments"
|
@getComments="getComments"
|
||||||
ref="issueComment"/>
|
ref="issueComment"/>
|
||||||
|
<ms-file-metadata-list ref="metadataList" @checkRows="checkRows"/>
|
||||||
|
<ms-file-batch-move ref="module" @setModuleId="setModuleId"/>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
|
|
||||||
</el-main>
|
</el-main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -193,7 +206,7 @@ import {
|
||||||
getCurrentProjectID,
|
getCurrentProjectID,
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
getCurrentUserId,
|
getCurrentUserId,
|
||||||
getCurrentWorkspaceId, getTypeByFileName, hasLicense,
|
getCurrentWorkspaceId, getTypeByFileName, getUUID, hasLicense,
|
||||||
} from "@/common/js/utils";
|
} from "@/common/js/utils";
|
||||||
import {enableThirdPartTemplate, getIssuePartTemplateWithProject, getPlatformTransitions} from "@/network/Issue";
|
import {enableThirdPartTemplate, getIssuePartTemplateWithProject, getPlatformTransitions} from "@/network/Issue";
|
||||||
import CustomFiledFormItem from "@/business/components/common/components/form/CustomFiledFormItem";
|
import CustomFiledFormItem from "@/business/components/common/components/form/CustomFiledFormItem";
|
||||||
|
@ -204,6 +217,8 @@ import {TokenKey} from "@/common/js/constants";
|
||||||
import {Message} from "element-ui";
|
import {Message} from "element-ui";
|
||||||
import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment";
|
import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
import MsFileMetadataList from "@/business/components/project/menu/file/quote/QuoteFileList";
|
||||||
|
import MsFileBatchMove from "@/business/components/project/menu/file/module/FileBatchMove";
|
||||||
|
|
||||||
const {getIssuesById} = require("@/network/Issue");
|
const {getIssuesById} = require("@/network/Issue");
|
||||||
|
|
||||||
|
@ -222,7 +237,9 @@ export default {
|
||||||
MsMarkDownText,
|
MsMarkDownText,
|
||||||
IssueComment,
|
IssueComment,
|
||||||
ReviewCommentItem,
|
ReviewCommentItem,
|
||||||
TestCaseAttachment
|
TestCaseAttachment,
|
||||||
|
MsFileMetadataList,
|
||||||
|
MsFileBatchMove,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -308,7 +325,10 @@ export default {
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
isDelete: true,
|
isDelete: true,
|
||||||
cancelFileToken: [],
|
cancelFileToken: [],
|
||||||
uploadFiles: []
|
uploadFiles: [],
|
||||||
|
relateFiles: [],
|
||||||
|
unRelateFiles: [],
|
||||||
|
dumpFile: {},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -362,6 +382,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
open(data, type) {
|
open(data, type) {
|
||||||
|
this.uploadFiles = [];
|
||||||
this.tabActiveName = 'relateTestCase'
|
this.tabActiveName = 'relateTestCase'
|
||||||
this.showFollow = false;
|
this.showFollow = false;
|
||||||
this.result.loading = true;
|
this.result.loading = true;
|
||||||
|
@ -546,8 +567,10 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
param.withoutTestCaseIssue = this.isMinder;
|
param.withoutTestCaseIssue = this.isMinder;
|
||||||
|
|
||||||
param.thirdPartPlatform = this.enableThirdPartTemplate;
|
param.thirdPartPlatform = this.enableThirdPartTemplate;
|
||||||
|
if (this.relateFiles.length > 0) {
|
||||||
|
param.relateFileMetaIds = this.relateFiles;
|
||||||
|
}
|
||||||
return param;
|
return param;
|
||||||
},
|
},
|
||||||
_save() {
|
_save() {
|
||||||
|
@ -651,7 +674,8 @@ export default {
|
||||||
progress: this.type === 'add' || this.isCaseEdit? 100 : 0,
|
progress: this.type === 'add' || this.isCaseEdit? 100 : 0,
|
||||||
status: this.type === 'add' || this.isCaseEdit? 'toUpload' : 0,
|
status: this.type === 'add' || this.isCaseEdit? 'toUpload' : 0,
|
||||||
creator: user.name,
|
creator: user.name,
|
||||||
type: getTypeByFileName(file.name)
|
type: getTypeByFileName(file.name),
|
||||||
|
isLocal: true
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.type === 'add' || this.isCaseEdit) {
|
if (this.type === 'add' || this.isCaseEdit) {
|
||||||
|
@ -753,7 +777,8 @@ export default {
|
||||||
this.fileList.splice(index, 1);
|
this.fileList.splice(index, 1);
|
||||||
this.tableData.splice(index, 1);
|
this.tableData.splice(index, 1);
|
||||||
if (this.type === 'add' || this.isCaseEdit) {
|
if (this.type === 'add' || this.isCaseEdit) {
|
||||||
this.uploadFiles.splice(index, 1);
|
let delIndex = this.uploadFiles.findIndex(uploadFile => uploadFile.name === file.name)
|
||||||
|
this.uploadFiles.splice(delIndex, 1);
|
||||||
} else {
|
} else {
|
||||||
this.$get('/attachment/delete/issue/' + file.id , response => {
|
this.$get('/attachment/delete/issue/' + file.id , response => {
|
||||||
this.$success(this.$t('commons.delete_success'));
|
this.$success(this.$t('commons.delete_success'));
|
||||||
|
@ -761,6 +786,38 @@ export default {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleUnRelate(file, index) {
|
||||||
|
// 取消关联
|
||||||
|
this.$alert(this.$t('load_test.unrelated_file_confirm') + file.name + "?", '', {
|
||||||
|
confirmButtonText: this.$t('commons.confirm'),
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
callback: (action) => {
|
||||||
|
if (action === 'confirm') {
|
||||||
|
let unRelateFileIndex = this.tableData.findIndex(f => f.name === file.name);
|
||||||
|
this.tableData.splice(unRelateFileIndex, 1);
|
||||||
|
if (file.status === 'toRelate') {
|
||||||
|
// 待关联的记录, 直接移除
|
||||||
|
let unRelateId = this.relateFiles.findIndex(f => f === file.id);
|
||||||
|
this.relateFiles.splice(unRelateId, 1);
|
||||||
|
} else {
|
||||||
|
// 已经关联的记录
|
||||||
|
this.unRelateFiles.push(file.id);
|
||||||
|
let data = {'belongType': 'issue', 'belongId': this.issueId, 'metadataRefIds': this.unRelateFiles};
|
||||||
|
this.result.loading = true;
|
||||||
|
this.$post('/attachment/metadata/unrelated', data, response => {
|
||||||
|
this.$success(this.$t('commons.unrelated_success'));
|
||||||
|
this.result.loading = false;
|
||||||
|
this.getFileMetaData(this.issueId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleDump(file) {
|
||||||
|
this.$refs.module.init();
|
||||||
|
this.dumpFile = file;
|
||||||
|
},
|
||||||
handleCancel(file, index) {
|
handleCancel(file, index) {
|
||||||
this.fileList.splice(index, 1);
|
this.fileList.splice(index, 1);
|
||||||
let cancelToken = this.cancelFileToken.filter(f => f.name === file.name)[0];
|
let cancelToken = this.cancelFileToken.filter(f => f.name === file.name)[0];
|
||||||
|
@ -769,7 +826,63 @@ export default {
|
||||||
cancelFile.progress = 100;
|
cancelFile.progress = 100;
|
||||||
cancelFile.status = 'error';
|
cancelFile.status = 'error';
|
||||||
},
|
},
|
||||||
|
associationFile() {
|
||||||
|
this.$refs.metadataList.open();
|
||||||
|
},
|
||||||
|
checkRows(rows) {
|
||||||
|
let repeatRecord = false;
|
||||||
|
for (let row of rows) {
|
||||||
|
let rowIndex = this.tableData.findIndex(item => item.name === row.name);
|
||||||
|
if (rowIndex >= 0) {
|
||||||
|
this.$error(this.$t('load_test.exist_related_file') + ": " + row.name);
|
||||||
|
repeatRecord = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!repeatRecord) {
|
||||||
|
if (this.type === 'add') {
|
||||||
|
// 新增
|
||||||
|
rows.forEach(row => {
|
||||||
|
this.relateFiles.push(row.id);
|
||||||
|
this.tableData.push({
|
||||||
|
id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
size: byteToSize(row.size),
|
||||||
|
updateTime: row.createTime,
|
||||||
|
progress: 100,
|
||||||
|
status: 'toRelate',
|
||||||
|
creator: row.createUser,
|
||||||
|
type: row.type,
|
||||||
|
isLocal: false,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 编辑
|
||||||
|
let metadataRefIds = [];
|
||||||
|
rows.forEach(row => metadataRefIds.push(row.id));
|
||||||
|
let data = {'belongType': 'issue', 'belongId': this.issueId, 'metadataRefIds': metadataRefIds};
|
||||||
|
this.result.loading = true;
|
||||||
|
this.$post('/attachment/metadata/relate', data, response => {
|
||||||
|
this.$success(this.$t('commons.relate_success'));
|
||||||
|
this.result.loading = false;
|
||||||
|
this.getFileMetaData(this.issueId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setModuleId(moduleId) {
|
||||||
|
let data = {id: getUUID(), resourceId: getCurrentProjectID(), moduleId: moduleId,
|
||||||
|
projectId: getCurrentProjectID(), fileName: this.dumpFile.name, attachmentId: this.dumpFile.id};
|
||||||
|
this.$post("/attachment/metadata/dump", data, (response) => {
|
||||||
|
this.$success(this.$t("organization.integration.successful_operation"));
|
||||||
|
});
|
||||||
|
},
|
||||||
getFileMetaData(id) {
|
getFileMetaData(id) {
|
||||||
|
if (this.type === 'edit') {
|
||||||
|
this.uploadFiles = [];
|
||||||
|
this.relateFiles = [];
|
||||||
|
this.unRelateFiles = [];
|
||||||
|
}
|
||||||
// 保存用例后传入用例id,刷新文件列表,可以预览和下载
|
// 保存用例后传入用例id,刷新文件列表,可以预览和下载
|
||||||
this.fileList = [];
|
this.fileList = [];
|
||||||
this.tableData = [];
|
this.tableData = [];
|
||||||
|
@ -838,4 +951,38 @@ export default {
|
||||||
font-size: xx-small;
|
font-size: xx-small;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-icon-plus {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-default {
|
||||||
|
background-color: #fbfdff;
|
||||||
|
border: 1px dashed #c0ccda;
|
||||||
|
border-radius: 6px;
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 40px;
|
||||||
|
height: 30px;
|
||||||
|
line-height: 32px;
|
||||||
|
vertical-align: top;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-default i {
|
||||||
|
color: #8c939d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-default:hover {
|
||||||
|
border: 1px dashed #783887;
|
||||||
|
}
|
||||||
|
|
||||||
|
.local-upload-tips {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
left: 25px;
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -72,6 +72,8 @@ export default {
|
||||||
warning_module_add: "Tree modules are up to 8 levels deep",
|
warning_module_add: "Tree modules are up to 8 levels deep",
|
||||||
send_success: 'Send successfully',
|
send_success: 'Send successfully',
|
||||||
delete_success: 'Deleted successfully',
|
delete_success: 'Deleted successfully',
|
||||||
|
relate_success: 'Related successfully',
|
||||||
|
unrelated_success: 'Unrelated successfully',
|
||||||
modify_success: 'Modify Success',
|
modify_success: 'Modify Success',
|
||||||
copy_success: 'Copy Success',
|
copy_success: 'Copy Success',
|
||||||
delete_cancel: 'Deleted Cancel',
|
delete_cancel: 'Deleted Cancel',
|
||||||
|
@ -188,6 +190,13 @@ export default {
|
||||||
month: "Month",
|
month: "Month",
|
||||||
year: "Year"
|
year: "Year"
|
||||||
},
|
},
|
||||||
|
file_upload_status: {
|
||||||
|
success: 'Success',
|
||||||
|
to_upload: 'To upload',
|
||||||
|
to_relate: 'To be associated',
|
||||||
|
expired: 'Expired',
|
||||||
|
error: 'Error'
|
||||||
|
},
|
||||||
test_unit: 'tests',
|
test_unit: 'tests',
|
||||||
remove: 'Remove',
|
remove: 'Remove',
|
||||||
next_level: "Next level",
|
next_level: "Next level",
|
||||||
|
@ -1098,6 +1107,7 @@ export default {
|
||||||
related_file_not_found: "No related test file found!",
|
related_file_not_found: "No related test file found!",
|
||||||
delete_file_when_uploading: 'The current operation may interrupt the file being uploaded!',
|
delete_file_when_uploading: 'The current operation may interrupt the file being uploaded!',
|
||||||
delete_file_confirm: 'Confirm delete file:',
|
delete_file_confirm: 'Confirm delete file:',
|
||||||
|
unrelated_file_confirm: 'Confirm unrelated file: ',
|
||||||
file_size_out_of_bounds: "File size out of bounds, file name: ",
|
file_size_out_of_bounds: "File size out of bounds, file name: ",
|
||||||
file_size_limit: "The number of files exceeds the limit!",
|
file_size_limit: "The number of files exceeds the limit!",
|
||||||
delete_file: "The file already exists, please delete the file with the same name first!",
|
delete_file: "The file already exists, please delete the file with the same name first!",
|
||||||
|
@ -1160,6 +1170,7 @@ export default {
|
||||||
report_type: 'Report type',
|
report_type: 'Report type',
|
||||||
upload_jmx: 'Upload JMX',
|
upload_jmx: 'Upload JMX',
|
||||||
exist_jmx: 'Existed Files',
|
exist_jmx: 'Existed Files',
|
||||||
|
exist_related_file: 'Existed Relate Files',
|
||||||
other_resource: 'Resource Files',
|
other_resource: 'Resource Files',
|
||||||
upload_file: 'Upload Files',
|
upload_file: 'Upload Files',
|
||||||
load_exist_file: 'Load Project Files',
|
load_exist_file: 'Load Project Files',
|
||||||
|
|
|
@ -137,7 +137,7 @@ export default {
|
||||||
cancel_relevance_project: "Disassociating the project will also cancel the associated test cases under the project",
|
cancel_relevance_project: "Disassociating the project will also cancel the associated test cases under the project",
|
||||||
img_loading_fail: "Image failed to load",
|
img_loading_fail: "Image failed to load",
|
||||||
pdf_loading_fail: "PDF loading failed",
|
pdf_loading_fail: "PDF loading failed",
|
||||||
upload_tip: "file size limit[0-500MB]",
|
upload_tip: "Local upload file size is limited to [0-500MB]",
|
||||||
add_attachment: "Add",
|
add_attachment: "Add",
|
||||||
attachment: "Attachment",
|
attachment: "Attachment",
|
||||||
upload_time: "Upload Time",
|
upload_time: "Upload Time",
|
||||||
|
|
|
@ -128,7 +128,7 @@ export default {
|
||||||
cancel_relevance_project: "取消项目关联会同时取消该项目下已关联的测试用例",
|
cancel_relevance_project: "取消项目关联会同时取消该项目下已关联的测试用例",
|
||||||
img_loading_fail: "图片加载失败",
|
img_loading_fail: "图片加载失败",
|
||||||
pdf_loading_fail: "PDF加载失败",
|
pdf_loading_fail: "PDF加载失败",
|
||||||
upload_tip: "文件大小限制[0-500MB]",
|
upload_tip: "本地上传, 文件大小限制[0-500MB]",
|
||||||
add_attachment: "添加",
|
add_attachment: "添加",
|
||||||
attachment: "附件",
|
attachment: "附件",
|
||||||
upload_time: "上传时间",
|
upload_time: "上传时间",
|
||||||
|
|
|
@ -128,7 +128,7 @@ export default {
|
||||||
cancel_relevance_project: "取消項目關聯會同時取消該項目下已關聯的測試用例",
|
cancel_relevance_project: "取消項目關聯會同時取消該項目下已關聯的測試用例",
|
||||||
img_loading_fail: "圖片加載失敗",
|
img_loading_fail: "圖片加載失敗",
|
||||||
pdf_loading_fail: "PDF加載失敗",
|
pdf_loading_fail: "PDF加載失敗",
|
||||||
upload_tip: "文件大小限制[0-500MB]",
|
upload_tip: "本地上傳, 文件大小限制[0-500MB]",
|
||||||
add_attachment: "添加",
|
add_attachment: "添加",
|
||||||
attachment: "附件",
|
attachment: "附件",
|
||||||
upload_time: "上傳時間",
|
upload_time: "上傳時間",
|
||||||
|
|
|
@ -71,6 +71,8 @@ export default {
|
||||||
warning_module_add: "模块树深度最大为8层",
|
warning_module_add: "模块树深度最大为8层",
|
||||||
send_success: '发送成功',
|
send_success: '发送成功',
|
||||||
delete_success: '删除成功',
|
delete_success: '删除成功',
|
||||||
|
relate_success: '关联成功',
|
||||||
|
unrelated_success: '取消关联成功',
|
||||||
copy_success: '复制成功',
|
copy_success: '复制成功',
|
||||||
modify_success: '修改成功',
|
modify_success: '修改成功',
|
||||||
delete_cancel: '已取消删除',
|
delete_cancel: '已取消删除',
|
||||||
|
@ -182,6 +184,13 @@ export default {
|
||||||
month: "月",
|
month: "月",
|
||||||
year: "年"
|
year: "年"
|
||||||
},
|
},
|
||||||
|
file_upload_status: {
|
||||||
|
success: '完成',
|
||||||
|
to_upload: '待上传',
|
||||||
|
to_relate: '待关联',
|
||||||
|
expired: '已失效',
|
||||||
|
error: '失败'
|
||||||
|
},
|
||||||
test_unit: '测试',
|
test_unit: '测试',
|
||||||
system_parameter_setting: '系统参数设置',
|
system_parameter_setting: '系统参数设置',
|
||||||
connection_successful: '连接成功',
|
connection_successful: '连接成功',
|
||||||
|
@ -1106,6 +1115,7 @@ export default {
|
||||||
related_file_not_found: "未找到关联的测试文件!",
|
related_file_not_found: "未找到关联的测试文件!",
|
||||||
delete_file_when_uploading: '当前操作可能会中断正在上传的文件!',
|
delete_file_when_uploading: '当前操作可能会中断正在上传的文件!',
|
||||||
delete_file_confirm: '确认删除文件: ',
|
delete_file_confirm: '确认删除文件: ',
|
||||||
|
unrelated_file_confirm: '确认取消关联: ',
|
||||||
file_size_limit: "文件个数超出限制!",
|
file_size_limit: "文件个数超出限制!",
|
||||||
file_size_out_of_bounds: "文件大小超出范围, 文件名称: ",
|
file_size_out_of_bounds: "文件大小超出范围, 文件名称: ",
|
||||||
delete_file: "文件已存在,请先删除同名文件!",
|
delete_file: "文件已存在,请先删除同名文件!",
|
||||||
|
@ -1171,6 +1181,7 @@ export default {
|
||||||
report_type: "报告类型",
|
report_type: "报告类型",
|
||||||
upload_jmx: '上传 JMX 文件',
|
upload_jmx: '上传 JMX 文件',
|
||||||
exist_jmx: '已存在的文件',
|
exist_jmx: '已存在的文件',
|
||||||
|
exist_related_file: '已存在的关联文件',
|
||||||
other_resource: '资源文件',
|
other_resource: '资源文件',
|
||||||
upload_file: '上传新文件',
|
upload_file: '上传新文件',
|
||||||
load_exist_file: '加载文件',
|
load_exist_file: '加载文件',
|
||||||
|
|
|
@ -70,6 +70,8 @@ export default {
|
||||||
save_success: '保存成功',
|
save_success: '保存成功',
|
||||||
send_success: '發送成功',
|
send_success: '發送成功',
|
||||||
delete_success: '刪除成功',
|
delete_success: '刪除成功',
|
||||||
|
relate_success: '關聯成功',
|
||||||
|
unrelated_success: '取消關聯成功',
|
||||||
copy_success: '復製成功',
|
copy_success: '復製成功',
|
||||||
warning_module_add: "模塊樹深度最大為8層",
|
warning_module_add: "模塊樹深度最大為8層",
|
||||||
modify_success: '修改成功',
|
modify_success: '修改成功',
|
||||||
|
@ -182,6 +184,13 @@ export default {
|
||||||
month: "月",
|
month: "月",
|
||||||
year: "年"
|
year: "年"
|
||||||
},
|
},
|
||||||
|
file_upload_status: {
|
||||||
|
success: '完成',
|
||||||
|
to_upload: '待上傳',
|
||||||
|
to_relate: '待關聯',
|
||||||
|
expired: '已失效',
|
||||||
|
error: '失敗'
|
||||||
|
},
|
||||||
test_unit: '測試',
|
test_unit: '測試',
|
||||||
system_parameter_setting: '系統參數設置',
|
system_parameter_setting: '系統參數設置',
|
||||||
connection_successful: '連接成功',
|
connection_successful: '連接成功',
|
||||||
|
@ -1102,6 +1111,7 @@ export default {
|
||||||
related_file_not_found: "未找到關聯的測試文件!",
|
related_file_not_found: "未找到關聯的測試文件!",
|
||||||
delete_file_when_uploading: '當前操作可能會中斷正在上傳的文件!',
|
delete_file_when_uploading: '當前操作可能會中斷正在上傳的文件!',
|
||||||
delete_file_confirm: '確認刪除文件: ',
|
delete_file_confirm: '確認刪除文件: ',
|
||||||
|
unrelated_file_confirm: '確認取消關聯: ',
|
||||||
file_size_limit: "文件個數超出限製!",
|
file_size_limit: "文件個數超出限製!",
|
||||||
file_size_out_of_bounds: "文件大小超出範圍, 文件名称: ",
|
file_size_out_of_bounds: "文件大小超出範圍, 文件名称: ",
|
||||||
delete_file: "文件已存在,請先刪除同名文件!",
|
delete_file: "文件已存在,請先刪除同名文件!",
|
||||||
|
@ -1167,6 +1177,7 @@ export default {
|
||||||
report_type: "报告类型",
|
report_type: "报告类型",
|
||||||
upload_jmx: '上傳 JMX 文件',
|
upload_jmx: '上傳 JMX 文件',
|
||||||
exist_jmx: '已存在的文件',
|
exist_jmx: '已存在的文件',
|
||||||
|
exist_related_file: '已存在的關聯文件',
|
||||||
other_resource: '資源文件',
|
other_resource: '資源文件',
|
||||||
upload_file: '上傳新文件',
|
upload_file: '上傳新文件',
|
||||||
load_exist_file: '加載文件',
|
load_exist_file: '加載文件',
|
||||||
|
|
Loading…
Reference in New Issue