Merge pull request #8754 from metersphere/pr@dev@fix_uploadissueimage

fix: 连续上传缺陷图片显示 bug
This commit is contained in:
zhangdahai112 2021-12-22 21:01:34 +08:00 committed by GitHub
commit 744308655e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 142 additions and 91 deletions

View File

@ -29,7 +29,7 @@ public class IssueCommentController {
@PostMapping("/save") @PostMapping("/save")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_REVIEW_READ_COMMENT) @RequiresPermissions(PermissionConstants.PROJECT_TRACK_REVIEW_READ_COMMENT)
@SendNotice(taskType = NoticeConstants.TaskType.DEFECT_TASK, target = "#targetClass.get(#request.issuesId)", targetClass = IssuesService.class, @SendNotice(taskType = NoticeConstants.TaskType.DEFECT_TASK, target = "#targetClass.get(#request.issuesId)", targetClass = IssuesService.class,
event = NoticeConstants.Event.COMMENT, mailTemplate = "track/IssuesCommentUpdate", subject = "缺陷评论更新通知") event = NoticeConstants.Event.COMMENT, mailTemplate = "track/IssuesCommentUpdate", subject = "缺陷")
public IssueComment saveComment(@RequestBody IssuesRelevanceRequest request) { public IssueComment saveComment(@RequestBody IssuesRelevanceRequest request) {
request.setId(UUID.randomUUID().toString()); request.setId(UUID.randomUUID().toString());
return issueCommentService.saveComment(request); return issueCommentService.saveComment(request);

View File

@ -10,7 +10,7 @@
:key="index" :key="index"
:comment="comment" :comment="comment"
:read-only="readOnly" :read-only="readOnly"
@refresh="getComments()"/> @refresh="getComments()" api-url="/test/case"/>
<div v-if="comments.length === 0" style="text-align: center"> <div v-if="comments.length === 0" style="text-align: center">
<i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;"> <i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;">
<span style="font-size: 15px; color: #8a8b8d;"> <span style="font-size: 15px; color: #8a8b8d;">

View File

@ -115,7 +115,7 @@
<review-comment-item v-for="(comment,index) in comments" <review-comment-item v-for="(comment,index) in comments"
:key="index" :key="index"
:comment="comment" :comment="comment"
@refresh="getComments"/> @refresh="getComments" api-url="/test/case"/>
<div v-if="comments.length === 0" style="text-align: center"> <div v-if="comments.length === 0" style="text-align: center">
<i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;"> <i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;">
<span style="font-size: 15px; color: #8a8b8d;"> <span style="font-size: 15px; color: #8a8b8d;">

View File

@ -102,7 +102,7 @@
<review-comment-item v-for="(comment,index) in comments" <review-comment-item v-for="(comment,index) in comments"
:key="index" :key="index"
:comment="comment" :comment="comment"
@refresh="getComments" :disabled="true"/> @refresh="getComments" :disabled="true" api-url="/test/case"/>
<div v-if="comments.length === 0" style="text-align: center"> <div v-if="comments.length === 0" style="text-align: center">
<i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;"> <i class="el-icon-chat-line-square" style="font-size: 15px;color: #8a8b8d;">
<span style="font-size: 15px; color: #8a8b8d;"> <span style="font-size: 15px; color: #8a8b8d;">

View File

@ -88,7 +88,7 @@ export default {
} }
this.result = this.$post('/issues/comment/save', comment, () => { this.result = this.$post('/issues/comment/save', comment, () => {
this.$success(this.$t('test_track.comment.send_success')); this.$success(this.$t('test_track.comment.send_success'));
this.refresh(comment.IssueId); this.refresh(comment.issuesId);
this.from.description = ''; this.from.description = '';
this.dialogTableVisible = false; this.dialogTableVisible = false;
}); });

View File

@ -29,10 +29,10 @@ export default {
} }
}, },
methods: { methods: {
open(data) { open(data, type) {
this.visible = true; this.visible = true;
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.issueEditDetail.open(data); this.$refs.issueEditDetail.open(data, type);
}) })
}, },
handleClose() { handleClose() {

View File

@ -235,8 +235,9 @@ export default {
}, },
}, },
methods: { methods: {
open(data) { open(data, type) {
this.result.loading = true; this.result.loading = true;
this.type = type;
this.$nextTick(() => { this.$nextTick(() => {
getIssuePartTemplateWithProject((template, project) => { getIssuePartTemplateWithProject((template, project) => {
this.currentProject = project; this.currentProject = project;
@ -244,17 +245,18 @@ export default {
}); });
}); });
if(data&&data.id){ if (data && data.id) {
this.$get('/issues/follow/' + data.id, response => { this.$get('/issues/follow/' + data.id, response => {
this.form.follows = response.data; this.form.follows = response.data;
for (let i = 0; i < response.data.length; i++) { for (let i = 0; i < response.data.length; i++) {
if(response.data[i]===this.currentUser().id){ if (response.data[i] === this.currentUser().id) {
this.showFollow = true; this.showFollow = true;
break; break;
} }
} }
}) })
}else { } else {
this.issueId = null;
this.form.follows = []; this.form.follows = [];
} }
}, },
@ -325,6 +327,7 @@ export default {
} }
} }
this.customFieldForm = parseCustomField(this.form, this.issueTemplate, this.customFieldRules); this.customFieldForm = parseCustomField(this.form, this.issueTemplate, this.customFieldRules);
this.comments = [];
this.$nextTick(() => { this.$nextTick(() => {
if (this.$refs.testCaseIssueList) { if (this.$refs.testCaseIssueList) {
this.$refs.testCaseIssueList.initTableData(); this.$refs.testCaseIssueList.initTableData();
@ -422,6 +425,10 @@ export default {
} }
}, },
openComment() { openComment() {
if (!this.issueId) {
this.$warning(this.$t('test_track.issue.save_before_open_comment'));
return;
}
this.$refs.issueComment.open(); this.$refs.issueComment.open();
}, },
getComments() { getComments() {

View File

@ -295,17 +295,17 @@ export default {
}, },
handleEdit(data) { handleEdit(data) {
this.$refs.issueEdit.open(data); this.$refs.issueEdit.open(data, 'edit');
}, },
handleCreate() { handleCreate() {
this.$refs.issueEdit.open(); this.$refs.issueEdit.open(null, 'add');
}, },
handleCopy(data) { handleCopy(data) {
let copyData = {}; let copyData = {};
Object.assign(copyData, data); Object.assign(copyData, data);
copyData.id = null; copyData.id = null;
copyData.name = data.name + '_copy'; copyData.name = data.name + '_copy';
this.$refs.issueEdit.open(copyData); this.$refs.issueEdit.open(copyData, 'copy');
}, },
handleDelete(data) { handleDelete(data) {
this.page.result = this.$get('issues/delete/' + data.id, () => { this.page.result = this.$get('issues/delete/' + data.id, () => {

View File

@ -19,46 +19,45 @@
</el-button> </el-button>
</span> </span>
<span class="comment-delete"> <span class="comment-delete">
<el-link icon="el-icon-edit" v-if="!isImage" style="font-size: 9px;margin-right: 6px;" @click="openEdit" :disabled="readOnly"/> <el-link icon="el-icon-edit" style="font-size: 9px;margin-right: 6px;" @click="openEdit" :disabled="readOnly"/>
<el-link icon="el-icon-close" @click="deleteComment" :disabled="readOnly"/> <el-link icon="el-icon-close" @click="deleteComment" :disabled="readOnly"/>
</span> </span>
<br/> <br/>
<!-- <div class="comment-desc" style="font-size: 10px;color: #303133">-->
<!-- <pre>{{ comment.description }}</pre>--> <div v-if="!isImage" class="comment-desc" style="font-size: 10px;color: #303133">
<!-- </div>-->
<div v-if="!isImage" class="comment-desc" style="font-size: 10px;color: #303133">
<pre>{{ comment.description }}</pre> <pre>{{ comment.description }}</pre>
</div> </div>
<div v-if="isImage" class="demo-image__preview"> <div v-if="isImage" class="demo-image__preview">
<pre>{{ imgDescription }}</pre> <pre>{{ imgDescription }}</pre>
<el-image <el-image
:z-index="imageIndex" :z-index="imageIndex"
style="width: 100px; height: 100px;" style="width: 100px; height: 100px;"
fit="contain" fit="contain"
:src="src" :src="src"
:preview-src-list="srcList"> :preview-src-list="srcList">
</el-image> </el-image>
</div> </div>
</div> </div>
<el-dialog <el-dialog :visible.sync="visible"
:title="$t('commons.edit')" :title="$t('commons.edit')"
:visible.sync="visible" :destroy-on-close="true"
width="30%" :close-on-click-modal="false"
:destroy-on-close="true" append-to-body>
:append-to-body="true"
:close-on-click-modal="false" <div>
show-close> <div class="editors_div_style">
<el-input <div id="editorsDiv">
type="textarea" <ms-mark-down-text prop="description" :data="comment" :toolbars="toolbars"/>
:rows="5" </div>
v-model="description"> </div>
</el-input> <div>
<span slot="footer" class="dialog-footer"> <el-button type="primary" size="mini" class="send-btn" @click="editComment">
<ms-dialog-footer {{ $t('test_track.comment.send') }}
@cancel="visible = false" </el-button>
@confirm="editComment"/> </div>
</span> </div>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
@ -66,10 +65,11 @@
<script> <script>
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter"; import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
import {getCurrentUser} from "@/common/js/utils"; import {getCurrentUser} from "@/common/js/utils";
import MsMarkDownText from "@/business/components/track/case/components/MsMarkDownText";
export default { export default {
name: "ReviewCommentItem", name: "ReviewCommentItem",
components: {MsDialogFooter}, components: {MsDialogFooter, MsMarkDownText},
props: { props: {
comment: Object, comment: Object,
readOnly: { readOnly: {
@ -83,16 +83,51 @@ export default {
return { return {
visible: false, visible: false,
imgDescription: "", imgDescription: "",
imageIndex:99999, imageIndex: 99999,
src:"", src: "",
srcList:[], srcList: [],
imgNameList:[], imgNameList: [],
description: "", description: "",
imageMatchPattern:"(\\!\\[)\\S+]\\(\\S+\\)", imageMatchPattern: "(\\!\\[)\\S+]\\(\\S+\\)",
toolbars: {
bold: false, //
italic: false, //
header: false, //
underline: false, // 线
strikethrough: false, // 线
mark: false, //
superscript: false, //
subscript: false, //
quote: false, //
ol: false, //
ul: false, //
link: false, //
imagelink: true, //
code: false, // code
table: false, //
fullscreen: false, //
readmodel: false, //
htmlcode: false, // html
help: false, //
/* 1.3.5 */
undo: false, //
redo: false, //
trash: false, //
save: false, // eventssave
/* 1.4.2 */
navigation: false, //
/* 2.1.8 */
alignleft: false, //
aligncenter: false, //
alignright: false, //
/* 2.2.1 */
subfield: false, //
preview: false, //
}
} }
}, },
computed:{ computed: {
isImage(){ isImage() {
return this.checkImage(this.comment.description); return this.checkImage(this.comment.description);
} }
}, },
@ -102,7 +137,7 @@ export default {
this.$warning(this.$t('test_track.comment.cannot_delete')); this.$warning(this.$t('test_track.comment.cannot_delete'));
return; return;
} }
if(this.imgNameList.length > 0){ if (this.imgNameList.length > 0) {
this.imgNameList.forEach(imgName => { this.imgNameList.forEach(imgName => {
this.$get('/resource/md/delete/' + imgName); this.$get('/resource/md/delete/' + imgName);
}); });
@ -121,90 +156,91 @@ export default {
this.visible = true; this.visible = true;
}, },
editComment() { editComment() {
this.$post(this.apiUrl + "/comment/edit", {id: this.comment.id, description: this.description}, () => { this.$post(this.apiUrl + "/comment/edit", {id: this.comment.id, description: this.comment.description}, () => {
this.visible = false; this.visible = false;
this.$success(this.$t('commons.modify_success')); this.$success(this.$t('commons.modify_success'));
this.$emit("refresh"); this.$emit("refresh");
}); });
}, },
checkImage(){ checkImage() {
this.srcList = [];
let param = this.comment.description; let param = this.comment.description;
let returnFlag = false; let returnFlag = false;
if(param){ if (param) {
let message = param+""; let message = param + "";
let matchIndex = message.indexOf("](/resource/md/get/"); let matchIndex = message.indexOf("](/resource/md/get/");
if(matchIndex > 0){ if (matchIndex > 0) {
let messageSplitArr = message.split("](/resource/md/get/"); let messageSplitArr = message.split("](/resource/md/get/");
for(let itemIndex = 0;itemIndex < messageSplitArr.length; itemIndex ++){ for (let itemIndex = 0; itemIndex < messageSplitArr.length; itemIndex++) {
let itemStr = messageSplitArr[itemIndex]; let itemStr = messageSplitArr[itemIndex];
let picNameIndex = itemStr.indexOf("!["); let picNameIndex = itemStr.indexOf("![");
if( picNameIndex < 0){ if (picNameIndex < 0) {
let endUrlIndex = itemStr.indexOf(")"); let endUrlIndex = itemStr.indexOf(")");
if( endUrlIndex > 0){ if (endUrlIndex > 0) {
let itemStrArr = itemStr.substr(0,endUrlIndex); let itemStrArr = itemStr.substr(0, endUrlIndex);
//if(imgNameList.) //if(imgNameList.)
if(this.imgNameList.indexOf(itemStrArr) < 0){ if (this.imgNameList.indexOf(itemStrArr) < 0) {
this.imgNameList.push(itemStrArr); this.imgNameList.push(itemStrArr);
} }
let imgUrl = "/resource/md/get/"+itemStrArr; let imgUrl = "/resource/md/get/" + itemStrArr;
this.src = imgUrl; this.src = imgUrl;
if(this.srcList.indexOf(itemStrArr) < 0){ if (this.srcList.indexOf(itemStrArr) < 0) {
this.srcList.push(imgUrl); this.srcList.push(imgUrl);
} }
} }
}else{ } else {
let inputStr = itemStr.substr(0,picNameIndex); let inputStr = itemStr.substr(0, picNameIndex);
if(this.imgDescription === ""){ if (this.imgDescription === "") {
this.imgDescription = inputStr; this.imgDescription = inputStr;
}else { } else {
this.imgDescription = "\n" + inputStr; this.imgDescription = "\n" + inputStr;
} }
} }
} }
}else{ } else {
let imgUrlIndex = message.indexOf("](http"); let imgUrlIndex = message.indexOf("](http");
if(imgUrlIndex > 0){ if (imgUrlIndex > 0) {
let imgUrlSplitArr = message.split("](http"); let imgUrlSplitArr = message.split("](http");
for(let itemIndex = 0;itemIndex < imgUrlSplitArr.length; itemIndex ++){ for (let itemIndex = 0; itemIndex < imgUrlSplitArr.length; itemIndex++) {
let itemStr = imgUrlSplitArr[itemIndex]; let itemStr = imgUrlSplitArr[itemIndex];
let picNameIndex = itemStr.indexOf("!["); let picNameIndex = itemStr.indexOf("![");
if( picNameIndex < 0){ if (picNameIndex < 0) {
let endUrlIndex = itemStr.indexOf(")"); let endUrlIndex = itemStr.indexOf(")");
if( endUrlIndex > 0){ if (endUrlIndex > 0) {
let itemStrArr = itemStr.substr(0,endUrlIndex); let itemStrArr = itemStr.substr(0, endUrlIndex);
//if(imgNameList.) //if(imgNameList.)
if(this.imgNameList.indexOf(itemStrArr) < 0){ if (this.imgNameList.indexOf(itemStrArr) < 0) {
this.imgNameList.push(itemStrArr); this.imgNameList.push(itemStrArr);
} }
let imgUrl = "http"+itemStrArr; let imgUrl = "http" + itemStrArr;
this.src = imgUrl; this.src = imgUrl;
if(this.srcList.indexOf(itemStrArr) < 0){ if (this.srcList.indexOf(itemStrArr) < 0) {
this.srcList.push(imgUrl); this.srcList.push(imgUrl);
} }
} }
}else{ } else {
let inputStr = itemStr.substr(0,picNameIndex); let inputStr = itemStr.substr(0, picNameIndex);
if(this.imgDescription === ""){ if (this.imgDescription === "") {
this.imgDescription = inputStr; this.imgDescription = inputStr;
}else { } else {
this.imgDescription = "\n" + inputStr; this.imgDescription = "\n" + inputStr;
} }
} }
} }
} }
} }
if(this.srcList.length > 0){ if (this.srcList.length > 0) {
returnFlag = true; returnFlag = true;
} }
} }
return returnFlag; return returnFlag;
}, },
checkByUrls(url){ checkByUrls(url) {
let checkResultFlag = false; let checkResultFlag = false;
if(this.imgNameList.length > 0){ if (this.imgNameList.length > 0) {
this.imgNameList.forEach(imgName => { this.imgNameList.forEach(imgName => {
if(imgName === url){ if (imgName === url) {
checkResultFlag = true; checkResultFlag = true;
} }
}); });
@ -269,6 +305,11 @@ pre {
} }
/deep/ .el-button--mini, .el-button--mini.is-round { /deep/ .el-button--mini, .el-button--mini.is-round {
padding: 4px 9px; padding: 7px 15px;
}
.send-btn {
margin-top: 5px;
width: 100%;
} }
</style> </style>

View File

@ -2085,7 +2085,7 @@ export default {
comment: { comment: {
no_comment: "No Comment", no_comment: "No Comment",
send_comment: "Post a comment (Ctrl + Enter to send)", send_comment: "Post a comment (Ctrl + Enter to send)",
send: "Send", send: "Confirm",
description_is_null: "Comment content cannot be empty!", description_is_null: "Comment content cannot be empty!",
send_success: "Comment successful!", send_success: "Comment successful!",
}, },
@ -2236,7 +2236,8 @@ export default {
third_party_integrated: "Third-party Platform Integrated", third_party_integrated: "Third-party Platform Integrated",
use_third_party: "Enable Jira Issue Template", use_third_party: "Enable Jira Issue Template",
update_third_party_bugs: "Update the defects of third-party platforms", update_third_party_bugs: "Update the defects of third-party platforms",
sync_bugs: "Synchronization Issue" sync_bugs: "Synchronization Issue",
save_before_open_comment: "Please save issue before comment",
}, },
report: { report: {
name: "Test Plan Report", name: "Test Plan Report",

View File

@ -2089,7 +2089,7 @@ export default {
comment: { comment: {
no_comment: "暂无评论", no_comment: "暂无评论",
send_comment: "发表评论Ctrl+Enter发送", send_comment: "发表评论Ctrl+Enter发送",
send: "发送", send: "确定",
description_is_null: "评论内容不能为空!", description_is_null: "评论内容不能为空!",
send_success: "评论成功!", send_success: "评论成功!",
cannot_edit: "无法编辑此评论!", cannot_edit: "无法编辑此评论!",
@ -2240,7 +2240,8 @@ export default {
third_party_integrated: "集成第三方平台", third_party_integrated: "集成第三方平台",
use_third_party: "使用 Jira 缺陷模板", use_third_party: "使用 Jira 缺陷模板",
update_third_party_bugs: "更新第三方平台的缺陷", update_third_party_bugs: "更新第三方平台的缺陷",
sync_bugs: "同步缺陷" sync_bugs: "同步缺陷",
save_before_open_comment: "请先保存缺陷再添加评论",
}, },
report: { report: {
name: "测试计划报告", name: "测试计划报告",

View File

@ -2089,7 +2089,7 @@ export default {
comment: { comment: {
no_comment: "暫無評論", no_comment: "暫無評論",
send_comment: "發表評論Ctrl+Enter發送", send_comment: "發表評論Ctrl+Enter發送",
send: "發送", send: "確定",
description_is_null: "評論內容不能為空!", description_is_null: "評論內容不能為空!",
send_success: "評論成功!", send_success: "評論成功!",
cannot_edit: "無法編輯此評論!", cannot_edit: "無法編輯此評論!",
@ -2240,7 +2240,8 @@ export default {
third_party_integrated: "集成第三方平臺", third_party_integrated: "集成第三方平臺",
use_third_party: "使用 Jira 缺陷模板", use_third_party: "使用 Jira 缺陷模板",
update_third_party_bugs: "更新第三方平臺的缺陷", update_third_party_bugs: "更新第三方平臺的缺陷",
sync_bugs: "同步缺陷" sync_bugs: "同步缺陷",
save_before_open_comment: "請先保存缺陷再添加評論",
}, },
report: { report: {
name: "測試計劃報告", name: "測試計劃報告",