refactor(测试跟踪): 优化用例版本对比
This commit is contained in:
parent
d0b7823d1b
commit
4357374353
|
@ -445,23 +445,6 @@
|
||||||
<!-- 底部操作按钮 -->
|
<!-- 底部操作按钮 -->
|
||||||
<div class="edit-footer-container" v-if="editable">
|
<div class="edit-footer-container" v-if="editable">
|
||||||
<template>
|
<template>
|
||||||
<!-- 保存 -->
|
|
||||||
<div
|
|
||||||
class="save-btn-row"
|
|
||||||
v-if="showAddBtn">
|
|
||||||
<el-button size="small" @click="handleCommand" :disabled="readOnly">
|
|
||||||
{{ $t("commons.save") }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<!-- 保存并添加到公共用例库 -->
|
|
||||||
<div
|
|
||||||
class="save-add-pub-row"
|
|
||||||
v-if="showPublic"
|
|
||||||
@click="handleCommand('ADD_AND_PUBLIC')">
|
|
||||||
<el-button size="small" :disabled="readOnly">
|
|
||||||
{{ $t("test_track.case.save_add_public") }}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
<!-- 保存并新建 -->
|
<!-- 保存并新建 -->
|
||||||
<div class="save-create-row">
|
<div class="save-create-row">
|
||||||
<el-button
|
<el-button
|
||||||
|
@ -469,13 +452,30 @@
|
||||||
@click="handleCommand('ADD_AND_CREATE')"
|
@click="handleCommand('ADD_AND_CREATE')"
|
||||||
v-if="showAddBtn"
|
v-if="showAddBtn"
|
||||||
:disabled="readOnly">
|
:disabled="readOnly">
|
||||||
{{ $t("test_track.case.save_create_continue") }}
|
{{ $t("case.saveAndCreate") }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 保存并添加到公共用例库 -->
|
||||||
|
<div
|
||||||
|
class="save-add-pub-row"
|
||||||
|
v-if="showPublic"
|
||||||
|
@click="handleCommand('ADD_AND_PUBLIC')">
|
||||||
|
<el-button size="small" :disabled="readOnly">
|
||||||
|
{{ $t("test_track.case.save_add_public") }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 保存 -->
|
||||||
|
<div
|
||||||
|
class="save-btn-row"
|
||||||
|
v-if="showAddBtn">
|
||||||
|
<el-button size="small" @click="handleCommand" :disabled="readOnly" type="primary">
|
||||||
|
{{ $t("commons.save") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog
|
<!-- <el-dialog
|
||||||
:fullscreen="true"
|
:fullscreen="true"
|
||||||
:visible.sync="dialogVisible"
|
:visible.sync="dialogVisible"
|
||||||
:destroy-on-close="true"
|
:destroy-on-close="true"
|
||||||
|
@ -488,7 +488,10 @@
|
||||||
:tree-nodes="treeNodes"
|
:tree-nodes="treeNodes"
|
||||||
></test-case-version-diff>
|
></test-case-version-diff>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
<version-create-other-info-select
|
-->
|
||||||
|
<!-- since v2.7 -->
|
||||||
|
<case-diff-side-viewer ref="caseDiffViewerRef" ></case-diff-side-viewer>
|
||||||
|
<version-create-other-info-select
|
||||||
@confirmOtherInfo="confirmOtherInfo"
|
@confirmOtherInfo="confirmOtherInfo"
|
||||||
ref="selectPropDialog"/>
|
ref="selectPropDialog"/>
|
||||||
|
|
||||||
|
@ -577,7 +580,7 @@ import {buildTree} from "metersphere-frontend/src/model/NodeTree";
|
||||||
import {versionEnableByProjectId} from "@/api/project";
|
import {versionEnableByProjectId} from "@/api/project";
|
||||||
import {openCaseEdit} from "@/business/case/test-case";
|
import {openCaseEdit} from "@/business/case/test-case";
|
||||||
import ListItemDeleteConfirm from "metersphere-frontend/src/components/ListItemDeleteConfirm";
|
import ListItemDeleteConfirm from "metersphere-frontend/src/components/ListItemDeleteConfirm";
|
||||||
|
import CaseDiffSideViewer from "./case/diff/CaseDiffSideViewer"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TestCaseEdit",
|
name: "TestCaseEdit",
|
||||||
|
@ -610,7 +613,8 @@ export default {
|
||||||
MsAsideContainer,
|
MsAsideContainer,
|
||||||
MsMainContainer,
|
MsMainContainer,
|
||||||
MxVersionHistory,
|
MxVersionHistory,
|
||||||
ListItemDeleteConfirm
|
ListItemDeleteConfirm,
|
||||||
|
CaseDiffSideViewer
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -1564,27 +1568,31 @@ export default {
|
||||||
}
|
}
|
||||||
return versionName;
|
return versionName;
|
||||||
},
|
},
|
||||||
async compareBranch(t1, t2) {
|
compareBranch(t1, t2) {
|
||||||
let t1Case = await testCaseGetByVersionId(t1.id, this.currentTestCaseInfo.id);
|
// 打开对比
|
||||||
let t2Case = await testCaseGetByVersionId(t2.id, this.currentTestCaseInfo.id);
|
this.dialogVisible = true;
|
||||||
|
this.$refs.caseDiffViewerRef.open(t1.id, t2.id, this.currentTestCaseInfo.id)
|
||||||
|
|
||||||
let p1 = getTestCase(t1Case.data.id);
|
// let t1Case = await testCaseGetByVersionId(t1.id, this.currentTestCaseInfo.id);
|
||||||
let p2 = getTestCase(t2Case.data.id);
|
// let t2Case = await testCaseGetByVersionId(t2.id, this.currentTestCaseInfo.id);
|
||||||
let that = this;
|
|
||||||
Promise.all([p1, p2]).then((r) => {
|
// let p1 = getTestCase(t1Case.data.id);
|
||||||
if (r[0] && r[1]) {
|
// let p2 = getTestCase(t2Case.data.id);
|
||||||
that.newData = r[0].data;
|
// let that = this;
|
||||||
that.oldData = r[1].data;
|
// Promise.all([p1, p2]).then((r) => {
|
||||||
that.newData.createTime = t1.createTime;
|
// if (r[0] && r[1]) {
|
||||||
that.oldData.createTime = t2.createTime;
|
// that.newData = r[0].data;
|
||||||
that.newData.versionName = t1.name;
|
// that.oldData = r[1].data;
|
||||||
that.oldData.versionName = t2.name;
|
// that.newData.createTime = t1.createTime;
|
||||||
that.newData.userName = t1Case.data.createName;
|
// that.oldData.createTime = t2.createTime;
|
||||||
that.oldData.userName = t2Case.data.createName;
|
// that.newData.versionName = t1.name;
|
||||||
this.setSpecialPropForCompare(that);
|
// that.oldData.versionName = t2.name;
|
||||||
that.dialogVisible = true;
|
// that.newData.userName = t1Case.data.createName;
|
||||||
}
|
// that.oldData.userName = t2Case.data.createName;
|
||||||
});
|
// this.setSpecialPropForCompare(that);
|
||||||
|
// that.dialogVisible = true;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
},
|
},
|
||||||
compare(row) {
|
compare(row) {
|
||||||
testCaseGetByVersionId(row.id, this.currentTestCaseInfo.refId).then(
|
testCaseGetByVersionId(row.id, this.currentTestCaseInfo.refId).then(
|
||||||
|
@ -2268,6 +2276,7 @@ export default {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
justify-content: flex-end;
|
||||||
// 底部按钮激活样式
|
// 底部按钮激活样式
|
||||||
.opt-active-primary {
|
.opt-active-primary {
|
||||||
background: #783887;
|
background: #783887;
|
||||||
|
@ -2287,7 +2296,7 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
.save-btn-row {
|
.save-btn-row {
|
||||||
margin-left: px2rem(24);
|
margin: 0 24px 0 12px;
|
||||||
el-button {
|
el-button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,18 @@
|
||||||
<img :src="iconSrc" alt="" />
|
<img :src="iconSrc" alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div class="detail">
|
<div class="detail">
|
||||||
<div class="filename">{{ fileItem.name }}</div>
|
<div class="filename">
|
||||||
|
<div
|
||||||
|
:class="
|
||||||
|
fileItem.diffStatus == 2 ? ['content', 'line-through'] : 'content'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ fileItem.name }}
|
||||||
|
</div>
|
||||||
|
<case-diff-status
|
||||||
|
:diffStatus="fileItem.diffStatus"
|
||||||
|
></case-diff-status>
|
||||||
|
</div>
|
||||||
<div class="file-info-row" v-if="isSuccess">
|
<div class="file-info-row" v-if="isSuccess">
|
||||||
<div class="size">{{ fileItem.size }}</div>
|
<div class="size">{{ fileItem.size }}</div>
|
||||||
<div class="split">|</div>
|
<div class="split">|</div>
|
||||||
|
@ -80,8 +91,12 @@
|
||||||
<script>
|
<script>
|
||||||
import {byteToSize, sizeToByte} from "@/business/utils/sdk-utils";
|
import {byteToSize, sizeToByte} from "@/business/utils/sdk-utils";
|
||||||
|
|
||||||
|
import CaseDiffStatus from "./diff/CaseDiffStatus";
|
||||||
export default {
|
export default {
|
||||||
name: "CaseAttachmentItem",
|
name: "CaseAttachmentItem",
|
||||||
|
components: {
|
||||||
|
CaseDiffStatus,
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
fileItem: Object,
|
fileItem: Object,
|
||||||
index: Number,
|
index: Number,
|
||||||
|
@ -340,12 +355,17 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
.filename {
|
.filename {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: #1f2329;
|
display: flex;
|
||||||
height: px2rem(22);
|
align-items: center;
|
||||||
line-height: px2rem(22);
|
.content {
|
||||||
white-space: nowrap;
|
width: 100%;
|
||||||
overflow: hidden;
|
color: #1f2329;
|
||||||
text-overflow: ellipsis;
|
height: px2rem(22);
|
||||||
|
line-height: px2rem(22);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.wait-upload {
|
.wait-upload {
|
||||||
height: px2rem(20);
|
height: px2rem(20);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<div class="header-img-row">{{ getShortName(comment.authorName) }}</div>
|
<div class="header-img-row">{{ getShortName(comment.authorName) }}</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="username">{{ comment.authorName }}</div>
|
<div class="username">{{ comment.authorName }}</div>
|
||||||
<div class="fiexed">{{$t('case.commented')}}</div>
|
<div class="fiexed">{{ $t("case.commented") }}</div>
|
||||||
<div class="time">{{ comment.createTime | datetimeFormat }}</div>
|
<div class="time">{{ comment.createTime | datetimeFormat }}</div>
|
||||||
|
|
||||||
<template v-if="!readOnly">
|
<template v-if="!readOnly">
|
||||||
|
@ -13,15 +13,22 @@
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<i class="el-icon-edit"></i>
|
<i class="el-icon-edit"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="label">{{$t('commons.edit')}}</div>
|
<div class="label">{{ $t("commons.edit") }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="remove opt-row" @click="deleteComment">
|
<div class="remove opt-row" @click="deleteComment">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<i class="el-icon-delete"></i>
|
<i class="el-icon-delete"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="label">{{$t('commons.delete')}}</div>
|
<div class="label">{{ $t("commons.delete") }}</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<div
|
||||||
|
class="status"
|
||||||
|
v-if="comment.diffStatus > 0"
|
||||||
|
style="margin-left: 5px"
|
||||||
|
>
|
||||||
|
<case-diff-status :diffStatus="comment.diffStatus"></case-diff-status>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="viewer">
|
<div class="viewer">
|
||||||
|
@ -38,10 +45,12 @@
|
||||||
import CaseCommentEdit from "./CaseCommentEdit";
|
import CaseCommentEdit from "./CaseCommentEdit";
|
||||||
import { getCurrentUser } from "metersphere-frontend/src/utils/token";
|
import { getCurrentUser } from "metersphere-frontend/src/utils/token";
|
||||||
import { deleteMarkDownImgByName } from "@/business/utils/sdk-utils";
|
import { deleteMarkDownImgByName } from "@/business/utils/sdk-utils";
|
||||||
|
import CaseDiffStatus from "./diff/CaseDiffStatus";
|
||||||
export default {
|
export default {
|
||||||
name: "CaseCommentViewItem",
|
name: "CaseCommentViewItem",
|
||||||
components: {
|
components: {
|
||||||
CaseCommentEdit,
|
CaseCommentEdit,
|
||||||
|
CaseDiffStatus,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
comment: Object,
|
comment: Object,
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
<template>
|
||||||
|
<div class="issue-wrap">
|
||||||
|
<ms-table
|
||||||
|
:show-select-all="false"
|
||||||
|
:data="tableData"
|
||||||
|
:fields.sync="fields"
|
||||||
|
:enable-selection="false"
|
||||||
|
ref="table"
|
||||||
|
>
|
||||||
|
<span v-for="item in fields" :key="item.key">
|
||||||
|
<!-- <ms-table-column
|
||||||
|
:label="$t('test_track.issue.id')"
|
||||||
|
:field="item"
|
||||||
|
prop="id"
|
||||||
|
v-if="false"
|
||||||
|
>
|
||||||
|
</ms-table-column> -->
|
||||||
|
<ms-table-column
|
||||||
|
:field="item"
|
||||||
|
:label="$t('ID')"
|
||||||
|
:sortable="true"
|
||||||
|
prop="num"
|
||||||
|
>
|
||||||
|
<template v-slot:default="{ row }">
|
||||||
|
<div :class="row.diffStatus == 2 ? 'line-through' : ''">
|
||||||
|
{{ row.num }}
|
||||||
|
</div>
|
||||||
|
<div style="width: 32px" v-if="row.diffStatus > 0">
|
||||||
|
<case-diff-status :diffStatus="row.diffStatus"></case-diff-status>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
:field="item"
|
||||||
|
:sortable="true"
|
||||||
|
:label="$t('test_track.issue.title')"
|
||||||
|
prop="title"
|
||||||
|
>
|
||||||
|
<template v-slot:default="{ row }">
|
||||||
|
<div :class="row.diffStatus == 2 ? 'line-through' : ''">
|
||||||
|
{{ row.title }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
:label="$t('test_track.issue.platform_status')"
|
||||||
|
:field="item"
|
||||||
|
v-if="isThirdPart"
|
||||||
|
prop="platformStatus"
|
||||||
|
>
|
||||||
|
<template v-slot="scope">
|
||||||
|
{{ scope.row.platformStatus ? scope.row.platformStatus : "--" }}
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
v-else
|
||||||
|
:field="item"
|
||||||
|
:label="$t('test_track.issue.status')"
|
||||||
|
prop="status"
|
||||||
|
>
|
||||||
|
<template v-slot="scope">
|
||||||
|
<span>{{
|
||||||
|
issueStatusMap[scope.row.status]
|
||||||
|
? issueStatusMap[scope.row.status]
|
||||||
|
: scope.row.status
|
||||||
|
}}</span>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<span v-for="field in issueTemplate.customFields" :key="field.id">
|
||||||
|
<ms-table-column
|
||||||
|
:field="item"
|
||||||
|
:label="field.name"
|
||||||
|
:prop="field.name"
|
||||||
|
v-if="field.name === '状态'"
|
||||||
|
>
|
||||||
|
<template v-slot="scope">
|
||||||
|
<el-dropdown
|
||||||
|
class="test-case-status"
|
||||||
|
@command="statusChange"
|
||||||
|
placement="bottom"
|
||||||
|
trigger="click"
|
||||||
|
>
|
||||||
|
<span class="el-dropdown-link">
|
||||||
|
{{
|
||||||
|
getCustomFieldValue(scope.row, field)
|
||||||
|
? getCustomFieldValue(scope.row, field)
|
||||||
|
: issueStatusMap[scope.row.status]
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<el-dropdown-menu slot="dropdown" chang>
|
||||||
|
<span v-for="(item, index) in status" :key="index">
|
||||||
|
<el-dropdown-item
|
||||||
|
:command="{ id: scope.row.id, status: item.value }"
|
||||||
|
>
|
||||||
|
{{ item.system ? $t(item.text) : item.text }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</span>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
:field="item"
|
||||||
|
:label="$t('test_track.issue.platform')"
|
||||||
|
prop="platform"
|
||||||
|
>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<issue-description-table-item :field="item" />
|
||||||
|
</span>
|
||||||
|
</ms-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import MsTable from "metersphere-frontend/src/components/new-ui/MsTable";
|
||||||
|
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
|
||||||
|
import CaseDiffStatus from "./CaseDiffStatus";
|
||||||
|
import {
|
||||||
|
getIssuePartTemplateWithProject,
|
||||||
|
getIssuesByCaseId,
|
||||||
|
getIssuesByCaseIdWithSearch,
|
||||||
|
} from "@/api/issue";
|
||||||
|
import {
|
||||||
|
getCustomFieldValue,
|
||||||
|
getTableHeaderWithCustomFields,
|
||||||
|
} from "metersphere-frontend/src/utils/tableUtils";
|
||||||
|
import { LOCAL } from "metersphere-frontend/src/utils/constants";
|
||||||
|
import IssueDescriptionTableItem from "@/business/issue/IssueDescriptionTableItem";
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffIssueRelate",
|
||||||
|
components: {
|
||||||
|
MsTableColumn,
|
||||||
|
MsTable,
|
||||||
|
CaseDiffStatus,
|
||||||
|
IssueDescriptionTableItem,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableData: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isThirdPart: false,
|
||||||
|
issueTemplate: {},
|
||||||
|
status: [],
|
||||||
|
issueRelateVisible: false,
|
||||||
|
fields: [],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
getIssuePartTemplateWithProject((template, project) => {
|
||||||
|
this.currentProject = project;
|
||||||
|
this.issueTemplate = template;
|
||||||
|
if (this.issueTemplate.platform === LOCAL) {
|
||||||
|
this.isThirdPart = false;
|
||||||
|
} else {
|
||||||
|
this.isThirdPart = true;
|
||||||
|
}
|
||||||
|
if (template) {
|
||||||
|
let customFields = template.customFields;
|
||||||
|
for (let fields of customFields) {
|
||||||
|
if (fields.name === "状态") {
|
||||||
|
this.status = fields.options;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.fields = getTableHeaderWithCustomFields(
|
||||||
|
"ISSUE_LIST",
|
||||||
|
this.issueTemplate.customFields
|
||||||
|
);
|
||||||
|
if (!this.isThirdPart) {
|
||||||
|
for (let i = 0; i < this.fields.length; i++) {
|
||||||
|
if (this.fields[i].id === "platformStatus") {
|
||||||
|
this.fields.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.$refs.table) {
|
||||||
|
this.$refs.table.reloadTable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.issue-wrap {
|
||||||
|
margin-top: 22px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,158 @@
|
||||||
|
|
||||||
|
<!--
|
||||||
|
依赖关系 容器
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="dependencies-container">
|
||||||
|
<!-- 图标展示 -->
|
||||||
|
<div class="dep-header-wrap" v-xpack>
|
||||||
|
<div class="header-row" @click="openGraph">
|
||||||
|
<div class="dep-icon">
|
||||||
|
<img
|
||||||
|
src="/assets/module/figma/icon_organization_outlined.svg"
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="dep-label">{{ $t("case.dependencies") }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 前置用例 -->
|
||||||
|
<div class="dep-pre-wrap">
|
||||||
|
<case-relationship-list
|
||||||
|
:title="
|
||||||
|
resourceType === 'TEST_CASE'
|
||||||
|
? $t('commons.relationship.pre_case')
|
||||||
|
: $t('commons.relationship.pre_api')
|
||||||
|
"
|
||||||
|
:tableData="preTableData"
|
||||||
|
relationship-type="PRE"
|
||||||
|
ref="preRelationshipList"
|
||||||
|
></case-relationship-list>
|
||||||
|
</div>
|
||||||
|
<!-- 后置用例 -->
|
||||||
|
<div class="dep-post-wrap">
|
||||||
|
<case-relationship-list
|
||||||
|
:title="
|
||||||
|
resourceType === 'TEST_CASE'
|
||||||
|
? $t('commons.relationship.post_case')
|
||||||
|
: $t('commons.relationship.post_api')
|
||||||
|
"
|
||||||
|
relationship-type="POST"
|
||||||
|
:tableData="postTableData"
|
||||||
|
ref="postRelationshipList"
|
||||||
|
></case-relationship-list>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mx-relationship-graph-drawer
|
||||||
|
v-xpack
|
||||||
|
v-permission
|
||||||
|
:graph-data="graphData"
|
||||||
|
@closeRelationGraph="closeRelationGraph"
|
||||||
|
ref="relationshipGraph"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MxRelationshipGraphDrawer from "metersphere-frontend/src/components/graph/RelationshipGraphDrawer";
|
||||||
|
import RelationshipList from "@/business/common/RelationshipList";
|
||||||
|
import { getRelationshipGraph } from "@/api/graph";
|
||||||
|
import CaseRelationshipList from "./CaseDiffRelationshipList";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffRelationship",
|
||||||
|
components: {
|
||||||
|
MxRelationshipGraphDrawer,
|
||||||
|
RelationshipList,
|
||||||
|
CaseRelationshipList,
|
||||||
|
},
|
||||||
|
props: ["resourceId", "resourceType", "readOnly", "versionEnable", "preTableData", "postTableData"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
graphData: {},
|
||||||
|
preCount: 0,
|
||||||
|
postCount: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open() {
|
||||||
|
this.$refs.preRelationshipList.getTableData();
|
||||||
|
this.$refs.postRelationshipList.getTableData();
|
||||||
|
},
|
||||||
|
openGraph() {
|
||||||
|
if (!this.resourceId) {
|
||||||
|
this.$warning(this.$t("api_test.automation.save_case_info"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
getRelationshipGraph(this.resourceId, this.resourceType).then((r) => {
|
||||||
|
this.graphData = r.data;
|
||||||
|
this.$refs.relationshipGraph.open();
|
||||||
|
this.$emit("openDependGraphDrawer", true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
closeRelationGraph() {
|
||||||
|
this.$emit("openDependGraphDrawer", false);
|
||||||
|
},
|
||||||
|
setPreCount(count) {
|
||||||
|
this.preCount = count;
|
||||||
|
this.$emit("setCount", this.preCount + this.postCount);
|
||||||
|
},
|
||||||
|
setPostCount(count) {
|
||||||
|
this.postCount = count;
|
||||||
|
this.$emit("setCount", this.preCount + this.postCount);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.left-icon {
|
||||||
|
width: 4%;
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 25px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/business/style/index.scss";
|
||||||
|
.dependencies-container {
|
||||||
|
overflow: hidden;
|
||||||
|
.dep-header-wrap {
|
||||||
|
margin-top: 24px;
|
||||||
|
width: 98px;
|
||||||
|
min-width: 98px;
|
||||||
|
height: 32px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #bbbfc4;
|
||||||
|
border-radius: 4px;
|
||||||
|
gap: 4px;
|
||||||
|
.header-row {
|
||||||
|
display: flex;
|
||||||
|
color: #1f2329;
|
||||||
|
margin-left: 12.58px;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
.dep-icon {
|
||||||
|
margin-right: 4.58px;
|
||||||
|
width: 14px;
|
||||||
|
height: 14px;
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dep-label {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dep-pre-wrap {
|
||||||
|
max-height: px2rem(269);
|
||||||
|
}
|
||||||
|
.dep-post-wrap {
|
||||||
|
max-height: px2rem(269);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
<template>
|
||||||
|
<div class="dep-container">
|
||||||
|
<div class="dep-header-wrap">
|
||||||
|
<div class="label-row">
|
||||||
|
<div class="control-open-opt" @click="expand = !expand">
|
||||||
|
<!-- 展开状态 -->
|
||||||
|
<i class="el-icon-caret-bottom" v-if="expand"></i>
|
||||||
|
<!-- 折叠状态 -->
|
||||||
|
<i class="el-icon-caret-right" v-else></i>
|
||||||
|
</div>
|
||||||
|
<div class="dep-title">{{ title }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dep-list-wrap" v-if="expand">
|
||||||
|
<test-case-relationship-list
|
||||||
|
:tableData="tableData"
|
||||||
|
:relationshipType="relationshipType"
|
||||||
|
ref="testCaseRelationshipList"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="split" v-else></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TestCaseRelationshipList from "./CaseDiffRelationshipTableList";
|
||||||
|
import { deleteRelationshipEdge } from "@/business/utils/sdk-utils";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffRelationshipList",
|
||||||
|
components: { TestCaseRelationshipList },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
expand: true,
|
||||||
|
result: {},
|
||||||
|
data: [],
|
||||||
|
options: [],
|
||||||
|
value: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableData: [],
|
||||||
|
title: String,
|
||||||
|
relationshipType: String,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/business/style/index.scss";
|
||||||
|
.dep-container {
|
||||||
|
margin-top: px2rem(24);
|
||||||
|
.dep-header-wrap {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: px2rem(13);
|
||||||
|
.label-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.control-open-opt {
|
||||||
|
margin-right: px2rem(9.25);
|
||||||
|
cursor: pointer;
|
||||||
|
color: #783887;
|
||||||
|
i {
|
||||||
|
width: px2rem(9.5);
|
||||||
|
height: px2rem(6.25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dep-title {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.opt-add-row {
|
||||||
|
.el-button--small {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #783887;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #783887;
|
||||||
|
height: 32px;
|
||||||
|
line-height: 32px;
|
||||||
|
padding: 0px 18.17px 0px 18.17px !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
:deep(.el-icon-plus:before) {
|
||||||
|
width: 11.67px;
|
||||||
|
height: 11.67px;
|
||||||
|
color: #783887;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.dep-list-wrap {
|
||||||
|
margin-top: px2rem(12);
|
||||||
|
}
|
||||||
|
.split {
|
||||||
|
height: 1px;
|
||||||
|
background-color: rgba(31, 35, 41, 0.15);
|
||||||
|
margin-top: px2rem(24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,249 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ms-table
|
||||||
|
:show-select-all="false"
|
||||||
|
:data="tableData"
|
||||||
|
:enable-selection="false"
|
||||||
|
ref="table"
|
||||||
|
:screen-height="null"
|
||||||
|
>
|
||||||
|
<ms-table-column
|
||||||
|
min-width="200px"
|
||||||
|
width="200px"
|
||||||
|
v-if="relationshipType === 'POST'"
|
||||||
|
:label="$t('commons.relationship.type')"
|
||||||
|
>
|
||||||
|
<template>
|
||||||
|
<div class="pos-label">
|
||||||
|
{{ $t("commons.relationship.current_case") }}
|
||||||
|
</div>
|
||||||
|
<div class="pos-type pos-left-margin">
|
||||||
|
{{ $t("commons.relationship.after_finish") }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
prop="targetCustomNum"
|
||||||
|
v-if="isCustomNum"
|
||||||
|
:label="$t('commons.id')"
|
||||||
|
sortable
|
||||||
|
min-width="100px"
|
||||||
|
width="100px"
|
||||||
|
>
|
||||||
|
<template v-slot:default="{ row }">
|
||||||
|
<div :class="row.diffStatus == 2 ? 'line-through' : ''">
|
||||||
|
{{ row.targetCustomNum }}
|
||||||
|
</div>
|
||||||
|
<div style="width: 32px" v-if="row.diffStatus > 0">
|
||||||
|
<case-diff-status :diffStatus="row.diffStatus"></case-diff-status>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
prop="targetNum"
|
||||||
|
v-else
|
||||||
|
:label="$t('commons.id')"
|
||||||
|
sortable
|
||||||
|
min-width="100px"
|
||||||
|
width="100px"
|
||||||
|
>
|
||||||
|
<template v-slot:default="{ row }">
|
||||||
|
<div :class="row.diffStatus == 2 ? 'line-through' : ''">
|
||||||
|
{{ row.targetNum }}
|
||||||
|
</div>
|
||||||
|
<div style="width: 32px" v-if="row.diffStatus > 0">
|
||||||
|
<case-diff-status :diffStatus="row.diffStatus"></case-diff-status>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
prop="targetName"
|
||||||
|
:label="$t('case.case_name')"
|
||||||
|
sortable
|
||||||
|
min-width="256px"
|
||||||
|
width="256px"
|
||||||
|
>
|
||||||
|
<template v-slot:default="{ row }">
|
||||||
|
<div :class="row.diffStatus == 2 ? 'line-through' : ''">
|
||||||
|
{{ row.targetName }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
v-xpack
|
||||||
|
prop="versionId"
|
||||||
|
:label="$t('commons.version')"
|
||||||
|
sortable
|
||||||
|
min-width="100px"
|
||||||
|
width="100px"
|
||||||
|
>
|
||||||
|
<template v-slot:default="scope">
|
||||||
|
<span>{{ scope.row.versionName }}</span>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
prop="creator"
|
||||||
|
:label="$t('commons.create_user')"
|
||||||
|
min-width="120"
|
||||||
|
>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
prop="status"
|
||||||
|
min-width="100px"
|
||||||
|
width="100px"
|
||||||
|
:label="$t('api_test.definition.api_case_status')"
|
||||||
|
>
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<status-table-item :value="$t(statusMap.get(row.status))" />
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
<ms-table-column
|
||||||
|
width="200px"
|
||||||
|
min-width="200px"
|
||||||
|
v-if="relationshipType === 'PRE'"
|
||||||
|
:label="$t('commons.relationship.type')"
|
||||||
|
>
|
||||||
|
<template>
|
||||||
|
<div class="pos-type pos-right-margin">
|
||||||
|
{{ $t("commons.relationship.after_finish") }}
|
||||||
|
</div>
|
||||||
|
<div class="pos-label">
|
||||||
|
{{ $t("commons.relationship.current_case") }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
</ms-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MsTable from "metersphere-frontend/src/components/new-ui/MsTable";
|
||||||
|
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
|
||||||
|
import MsTableSearchBar from "metersphere-frontend/src/components/MsTableSearchBar";
|
||||||
|
import RelationshipFunctionalRelevance from "../../common/CaseRelationshipFunctionalRelevance";
|
||||||
|
import { getRelationshipCase } from "@/api/testCase";
|
||||||
|
import StatusTableItem from "@/business/common/tableItems/planview/StatusTableItem";
|
||||||
|
import { useStore } from "@/store";
|
||||||
|
import { operationConfirm } from "metersphere-frontend/src/utils";
|
||||||
|
import CaseDiffStatus from "./CaseDiffStatus";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffRelationshipTableList",
|
||||||
|
components: {
|
||||||
|
RelationshipFunctionalRelevance,
|
||||||
|
MsTableSearchBar,
|
||||||
|
MsTableColumn,
|
||||||
|
MsTable,
|
||||||
|
StatusTableItem,
|
||||||
|
CaseDiffStatus,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
result: {},
|
||||||
|
data: [],
|
||||||
|
operators: [
|
||||||
|
{
|
||||||
|
tip: this.$t("case.relieve"),
|
||||||
|
isTextButton: true,
|
||||||
|
exec: this.handleDelete,
|
||||||
|
isDisable: this.readOnly,
|
||||||
|
permissions: ["PROJECT_TRACK_CASE:READ+DELETE"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
condition: {},
|
||||||
|
options: [],
|
||||||
|
statusMap: new Map(),
|
||||||
|
value: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableData: [],
|
||||||
|
readOnly: Boolean,
|
||||||
|
relationshipType: String,
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isCustomNum() {
|
||||||
|
let template = useStore().testCaseTemplate;
|
||||||
|
if (template && template.customFields) {
|
||||||
|
template.customFields.forEach((item) => {
|
||||||
|
if (item.name === "用例状态") {
|
||||||
|
for (let i = 0; i < item.options.length; i++) {
|
||||||
|
this.statusMap.set(item.options[i].value, item.options[i].text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return useStore().currentProjectIsCustomNum;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTableData() {
|
||||||
|
getRelationshipCase(this.caseId, this.relationshipType).then((r) => {
|
||||||
|
this.data = r.data;
|
||||||
|
this.$emit("setCount", this.data.length);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
openRelevance() {
|
||||||
|
this.$refs.testCaseRelevance.open();
|
||||||
|
},
|
||||||
|
handleDelete(item) {
|
||||||
|
operationConfirm(
|
||||||
|
this,
|
||||||
|
this.$t("test_track.case.delete_confirm") + "依赖吗 ?",
|
||||||
|
() => {
|
||||||
|
this.$emit("deleteRelationship", item.sourceId, item.targetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.type-type {
|
||||||
|
color: var(--primary_color);
|
||||||
|
font-style: var(--primary_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.type-type:nth-child(2) {
|
||||||
|
margin: 0px 10px;
|
||||||
|
}
|
||||||
|
.pos-type {
|
||||||
|
width: auto;
|
||||||
|
height: 22px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
text-align: center;
|
||||||
|
color: #2ea121;
|
||||||
|
padding: 0 6px;
|
||||||
|
display: inline-block;
|
||||||
|
background: rgba(52, 199, 36, 0.1);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.pos-label {
|
||||||
|
display: inline-block;
|
||||||
|
height: 22px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
.pos-left-margin {
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
.pos-right-margin {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
.line-through {
|
||||||
|
text-decoration-line: line-through;
|
||||||
|
color: #8f959e !important;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,122 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-drawer
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:visible.sync="visible"
|
||||||
|
:size="widthCacl"
|
||||||
|
@close="close"
|
||||||
|
destroy-on-close
|
||||||
|
:full-screen="isFullScreen"
|
||||||
|
ref="relevanceDialog"
|
||||||
|
custom-class="file-drawer"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<template slot="title">
|
||||||
|
<div style="color: #1f2329; font-size: 16px; font-weight: 500">
|
||||||
|
{{ dialogTitle }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<case-diff-viewer
|
||||||
|
:versionLeftId="versionLeftId"
|
||||||
|
:versionRightId="versionRightId"
|
||||||
|
:caseId="caseId"
|
||||||
|
></case-diff-viewer>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import CaseDiffViewer from "@/business/case/components/case/diff/CaseDiffViewer";
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffSideViewer",
|
||||||
|
components: { CaseDiffViewer },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false,
|
||||||
|
isFullScreen: false,
|
||||||
|
// props 数据
|
||||||
|
versionLeftId: "",
|
||||||
|
versionRightId: "",
|
||||||
|
caseId: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 1200,
|
||||||
|
},
|
||||||
|
dialogTitle: {
|
||||||
|
type: String,
|
||||||
|
default() {
|
||||||
|
return this.$t("case.version_comparison");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
widthCacl() {
|
||||||
|
if (!isNaN(this.width)) {
|
||||||
|
//计算rem
|
||||||
|
let remW = (this.width / 1440) * 100;
|
||||||
|
let standW = (1200 / 1440) * 100;
|
||||||
|
return remW > standW ? remW : standW + "%";
|
||||||
|
}
|
||||||
|
return this.width;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(versionLeftId, versionRightId, caseId) {
|
||||||
|
this.versionLeftId = versionLeftId;
|
||||||
|
this.versionRightId = versionRightId;
|
||||||
|
this.caseId = caseId;
|
||||||
|
this.visible = true;
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.visible = false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/business/style/index.scss";
|
||||||
|
.content-box {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.body-wrap {
|
||||||
|
display: flex;
|
||||||
|
/* height: px2rem(763); */
|
||||||
|
/* min-height: px2rem(763); */
|
||||||
|
flex: 9;
|
||||||
|
.aside-wrap {
|
||||||
|
width: px2rem(268);
|
||||||
|
border-right: 1px solid rgba(31, 35, 41, 0.15);
|
||||||
|
padding: px2rem(24) px2rem(24) 0 px2rem(24);
|
||||||
|
}
|
||||||
|
.content-wrap {
|
||||||
|
width: px2rem(930);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.footer-wrap {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: px2rem(80);
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px -1px 4px rgba(31, 35, 41, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-wrap .options {
|
||||||
|
height: 80px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0px -1px 4px rgba(31, 35, 41, 0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.footer-wrap .options-btn {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 24px;
|
||||||
|
height: 32px;
|
||||||
|
margin-right: 24px;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,80 @@
|
||||||
|
<template>
|
||||||
|
<div class="diff-wrap">
|
||||||
|
<div class="status diff-create" v-if="diffStatus == 1">
|
||||||
|
{{ getDIffContent(diffStatus) }}
|
||||||
|
</div>
|
||||||
|
<div class="status diff-delete" v-if="diffStatus == 2">
|
||||||
|
{{ getDIffContent(diffStatus) }}
|
||||||
|
</div>
|
||||||
|
<div class="status diff-change" v-if="diffStatus == 3">
|
||||||
|
{{ getDIffContent(diffStatus) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffStatus",
|
||||||
|
props: {
|
||||||
|
diffStatus: Number,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDIffContent(status) {
|
||||||
|
if (status == 1) {
|
||||||
|
return "新建";
|
||||||
|
}
|
||||||
|
if (status == 2) {
|
||||||
|
return "删除";
|
||||||
|
}
|
||||||
|
if (status == 3) {
|
||||||
|
return "格式变化";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.diff-create {
|
||||||
|
min-width: 32px;
|
||||||
|
padding: 0px 4px;
|
||||||
|
height: 16px;
|
||||||
|
background: rgba(52, 199, 36, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: #2ea121;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.diff-delete {
|
||||||
|
min-width: 32px;
|
||||||
|
padding: 0px 4px;
|
||||||
|
height: 16px;
|
||||||
|
background: rgba(245, 74, 69, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: #d83931;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.diff-change {
|
||||||
|
min-width: 56px;
|
||||||
|
padding: 0px 4px;
|
||||||
|
height: 16px;
|
||||||
|
background: rgba(255, 136, 0, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: #de7802;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.line-through {
|
||||||
|
text-decoration-line: line-through;
|
||||||
|
color: #8f959e !important;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,109 @@
|
||||||
|
<template>
|
||||||
|
<div class="diff-box">
|
||||||
|
<ms-table
|
||||||
|
:show-select-all="false"
|
||||||
|
:data="tableData"
|
||||||
|
:enable-selection="false"
|
||||||
|
>
|
||||||
|
<ms-table-column
|
||||||
|
prop="num"
|
||||||
|
label="ID"
|
||||||
|
sortable
|
||||||
|
min-width="100px"
|
||||||
|
width="100px"
|
||||||
|
>
|
||||||
|
<template v-slot:default="{ row }">
|
||||||
|
<div :class="row.diffStatus == 2 ? 'line-through' : ''">
|
||||||
|
{{ row.num }}
|
||||||
|
</div>
|
||||||
|
<div style="width: 32px" v-if="row.diffStatus > 0">
|
||||||
|
<case-diff-status :diffStatus="row.diffStatus"></case-diff-status>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
prop="name"
|
||||||
|
:label="$t('case.case_name')"
|
||||||
|
min-width="316px"
|
||||||
|
width="316px"
|
||||||
|
sortable
|
||||||
|
>
|
||||||
|
<template v-slot:default="{ row }">
|
||||||
|
<div :class="row.diffStatus == 2 ? 'line-through' : ''">
|
||||||
|
{{ row.name }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
prop="projectName"
|
||||||
|
:label="$t('commons.project')"
|
||||||
|
sortable
|
||||||
|
min-width="180px"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ms-table-column
|
||||||
|
v-xpack
|
||||||
|
sortable
|
||||||
|
prop="versionName"
|
||||||
|
:label="$t('commons.version')"
|
||||||
|
min-width="100px"
|
||||||
|
width="100px"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ms-table-column :label="$t('test_resource_pool.type')" prop="type">
|
||||||
|
<template v-slot:default="{ row }">
|
||||||
|
{{ typeMap[row.testType] }}
|
||||||
|
</template>
|
||||||
|
</ms-table-column>
|
||||||
|
</ms-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import MsTable from "metersphere-frontend/src/components/new-ui/MsTable";
|
||||||
|
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
|
||||||
|
import CaseDiffStatus from "./CaseDiffStatus";
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffTestRelate",
|
||||||
|
components: {
|
||||||
|
MsTableColumn,
|
||||||
|
MsTable,
|
||||||
|
CaseDiffStatus,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
tableData: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
typeMap: {
|
||||||
|
testcase: this.$t(
|
||||||
|
"api_test.home_page.failed_case_list.table_value.case_type.api"
|
||||||
|
),
|
||||||
|
automation: this.$t(
|
||||||
|
"api_test.home_page.failed_case_list.table_value.case_type.scene"
|
||||||
|
),
|
||||||
|
performance: this.$t(
|
||||||
|
"api_test.home_page.failed_case_list.table_value.case_type.load"
|
||||||
|
),
|
||||||
|
uiAutomation: this.$t(
|
||||||
|
"api_test.home_page.failed_case_list.table_value.case_type.ui"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.diff-box {
|
||||||
|
margin-top: 22px;
|
||||||
|
}
|
||||||
|
.line-through {
|
||||||
|
text-decoration-line: line-through;
|
||||||
|
color: #8f959e !important;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,180 @@
|
||||||
|
<!--
|
||||||
|
文本类型对比
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="diff-box">
|
||||||
|
<div v-for="(info, i) in diffInfo" :key="i">
|
||||||
|
<div
|
||||||
|
class="diff-container"
|
||||||
|
v-for="(item, index) in info.diffArr"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<div class="change-wrap">
|
||||||
|
<div
|
||||||
|
:class="getStatusClassByType(item.status)"
|
||||||
|
v-if="item.status != 0 && item.status != 3"
|
||||||
|
>
|
||||||
|
{{ getStatusLabel(item.status) }}
|
||||||
|
</div>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div
|
||||||
|
:class="getStatusClassByType(item.status)"
|
||||||
|
v-if="item.status == 3"
|
||||||
|
>
|
||||||
|
{{ getStatusLabel(item.status) }}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
item.body.type === 'ARRAY' &&
|
||||||
|
Array.isArray(item.body.content) &&
|
||||||
|
item.body.content.length > 0
|
||||||
|
"
|
||||||
|
class="array-box"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
:class="
|
||||||
|
checkDelete(item.status) ? ['array', 'array-delete'] : 'array'
|
||||||
|
"
|
||||||
|
v-for="(e, i) in item.body.content"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
{{ e }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-else
|
||||||
|
:class="
|
||||||
|
checkDelete(item.status) ? ['text', 'line-through'] : 'text'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{
|
||||||
|
item.body.content == "" ||
|
||||||
|
item.body.content == null ||
|
||||||
|
item.body.content == undefined ||
|
||||||
|
(Array.isArray(item.body.content) &&
|
||||||
|
item.body.content.length == 0)
|
||||||
|
? "暂无"
|
||||||
|
: item.body.content
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffText",
|
||||||
|
props: {
|
||||||
|
diffInfo: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
// 0-正常 1-新增 2-删除 3-格式变化
|
||||||
|
diffArr: [
|
||||||
|
{
|
||||||
|
status: 0,
|
||||||
|
body: {
|
||||||
|
type: "TEXT",
|
||||||
|
content: this.$t("case.none"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
diffType: "TEXT",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
statusMap: new Map([
|
||||||
|
[0, ""],
|
||||||
|
[1, "create-row"],
|
||||||
|
[2, "delete-row"],
|
||||||
|
[3, "format-change-row"],
|
||||||
|
]),
|
||||||
|
statusConvertLabelMap: new Map([
|
||||||
|
[1, this.$t("新建")],
|
||||||
|
[2, this.$t("删除")],
|
||||||
|
[3, this.$t("格式变化")],
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getStatusClassByType(type) {
|
||||||
|
let arr = [];
|
||||||
|
// arr.push("status-row");
|
||||||
|
arr.push(this.statusMap.get(type));
|
||||||
|
return arr;
|
||||||
|
},
|
||||||
|
getStatusLabel(type) {
|
||||||
|
return this.statusConvertLabelMap.get(type);
|
||||||
|
},
|
||||||
|
checkDelete(type) {
|
||||||
|
return type === 2;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.diff-box {
|
||||||
|
.diff-container {
|
||||||
|
.change-wrap {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 8px;
|
||||||
|
.status-row {
|
||||||
|
width: 32px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-wrap {
|
||||||
|
margin-left: 2px;
|
||||||
|
.text {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.array-box {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 196px;
|
||||||
|
|
||||||
|
.array {
|
||||||
|
padding: 1px 6px;
|
||||||
|
width: 82px;
|
||||||
|
height: 24px;
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
background: rgba(120, 56, 135, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #783887;
|
||||||
|
line-height: 22px;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.array-delete {
|
||||||
|
padding: 1px 6px;
|
||||||
|
width: 82px;
|
||||||
|
height: 24px;
|
||||||
|
margin-left: 4px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
background: rgba(120, 56, 135, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #783887;
|
||||||
|
line-height: 22px;
|
||||||
|
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,974 @@
|
||||||
|
<template>
|
||||||
|
<div class="diff-box">
|
||||||
|
<div class="switch-version-container">
|
||||||
|
<div class="prev-row" @click.stop="prev" v-show="enablePrev">
|
||||||
|
<i class="el-icon-arrow-left"></i>
|
||||||
|
</div>
|
||||||
|
<div class="version-viewer-row">
|
||||||
|
<div
|
||||||
|
class="tab-list"
|
||||||
|
:style="{
|
||||||
|
transform: 'translateX( ' + translateX + 'px)',
|
||||||
|
transition: '0.5s',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="version-item-row"
|
||||||
|
v-for="item in versionList"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<div class="version-label">
|
||||||
|
<div class="label">{{ item.name }}</div>
|
||||||
|
<div class="is-new" v-if="item.latest">
|
||||||
|
{{ $t("case.last_version") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="version-creator">
|
||||||
|
<div class="username">{{ item.createUserName }}</div>
|
||||||
|
<div class="static-label">创建</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="next-row" @click.stop="next" v-show="enableNext">
|
||||||
|
<i class="el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="version-detail-diff-container content-body-wrap">
|
||||||
|
<div class="tab-pane-wrap">
|
||||||
|
<el-tabs v-model="caseActiveName" @tab-click="tabClick">
|
||||||
|
<el-tab-pane :label="$t('case.use_case_detail')" name="detail">
|
||||||
|
<div class="content-conatiner">
|
||||||
|
<div class="case-name-row">
|
||||||
|
<div class="case-name case-title-wrap case-content-wrap">
|
||||||
|
<div class="name title-wrap">{{ $t("case.case_name") }}</div>
|
||||||
|
<div class="required required-item"></div>
|
||||||
|
</div>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="opt-row">
|
||||||
|
<case-diff-text
|
||||||
|
:diffInfo="contentDiffData.caseName"
|
||||||
|
></case-diff-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- pre condition -->
|
||||||
|
<div class="pre-condition-row">
|
||||||
|
<div class="condition-name case-title-wrap case-content-wrap">
|
||||||
|
<div class="name title-wrap">
|
||||||
|
{{ $t("case.preconditions") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="opt-row">
|
||||||
|
<case-diff-text
|
||||||
|
:diffInfo="contentDiffData.prerequisite"
|
||||||
|
></case-diff-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- step description -->
|
||||||
|
<div class="step-desc-row">
|
||||||
|
<!-- 类型切换 -->
|
||||||
|
<div class="step-desc-name case-title-wrap case-content-wrap">
|
||||||
|
<div class="name title-wrap">
|
||||||
|
{{
|
||||||
|
contentDiffData.stepModel === "TEXT"
|
||||||
|
? $t("test_track.case.text_describe")
|
||||||
|
: $t("test_track.case.step_describe")
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
<div class="update-type-row title-wrap"></div>
|
||||||
|
</div>
|
||||||
|
<!-- 文本描述 -->
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="opt-row">
|
||||||
|
<case-diff-text
|
||||||
|
:diffInfo="contentDiffData.stepDescription"
|
||||||
|
></case-diff-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- expect -->
|
||||||
|
<div
|
||||||
|
class="expect-row"
|
||||||
|
v-if="contentDiffData.stepModel === 'TEXT'"
|
||||||
|
>
|
||||||
|
<div class="expect-name case-title-wrap case-content-wrap">
|
||||||
|
<div class="name title-wrap">
|
||||||
|
{{ $t("test_track.case.expected_results") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="opt-row">
|
||||||
|
<case-diff-text
|
||||||
|
:diffInfo="contentDiffData.expectedResult"
|
||||||
|
></case-diff-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- remark -->
|
||||||
|
<div class="remark-row">
|
||||||
|
<div class="remark-name case-title-wrap case-content-wrap">
|
||||||
|
<div class="name title-wrap">{{ $t("commons.remark") }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<div class="opt-row">
|
||||||
|
<case-diff-text
|
||||||
|
:diffInfo="contentDiffData.remark"
|
||||||
|
></case-diff-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 附件 -->
|
||||||
|
<div class="attachment-row">
|
||||||
|
<div class="attachment-name case-title-wrap case-content-wrap">
|
||||||
|
<div class="name title-wrap">{{ $t("case.attachment") }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="content-wrap">
|
||||||
|
<!-- 添加附件 -->
|
||||||
|
<!-- tip -->
|
||||||
|
<div class="opt-btn">
|
||||||
|
<div class="attachment-preview">
|
||||||
|
<case-attachment-viewer
|
||||||
|
:tableData="attachmentDiffData"
|
||||||
|
:isCopy="false"
|
||||||
|
:readOnly="true"
|
||||||
|
:is-delete="false"
|
||||||
|
v-if="
|
||||||
|
attachmentDiffData && attachmentDiffData.lenght > 0
|
||||||
|
"
|
||||||
|
></case-attachment-viewer>
|
||||||
|
<div v-else>{{ $t("case.none") }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<!-- 关联测试用例 -->
|
||||||
|
<el-tab-pane
|
||||||
|
:label="$t('case.associate_test_cases')"
|
||||||
|
name="associateTestCases"
|
||||||
|
>
|
||||||
|
<div class="content-conatiner">
|
||||||
|
<case-diff-test-relate
|
||||||
|
:tableData="testCaseRelateData"
|
||||||
|
></case-diff-test-relate>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<!-- 关联缺陷 -->
|
||||||
|
<el-tab-pane
|
||||||
|
:label="$t('test_track.case.relate_issue')"
|
||||||
|
name="associatedDefects"
|
||||||
|
>
|
||||||
|
<div class="content-conatiner">
|
||||||
|
<case-diff-issue-relate
|
||||||
|
:tableData="issuesData"
|
||||||
|
></case-diff-issue-relate>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<!-- 依赖关系 -->
|
||||||
|
<el-tab-pane :label="$t('case.dependencies')" name="dependencies">
|
||||||
|
<div class="content-conatiner">
|
||||||
|
<case-diff-relationship
|
||||||
|
:resourceId="caseId"
|
||||||
|
:preTableData="preTableData"
|
||||||
|
:postTableData="postTableData"
|
||||||
|
></case-diff-relationship>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<!-- 评论 -->
|
||||||
|
<el-tab-pane :label="$t('case.comment')" name="comment">
|
||||||
|
<div class="content-conatiner">
|
||||||
|
<case-comment-viewer
|
||||||
|
:readOnly="true"
|
||||||
|
:comments="diffCommentsData"
|
||||||
|
></case-comment-viewer>
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
<!-- 变更记录 -->
|
||||||
|
<!-- <el-tab-pane :label="$t('case.change_record')" name="changeRecord">
|
||||||
|
<div class="content-conatiner"></div>
|
||||||
|
</el-tab-pane> -->
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
<div class="base-info-wrap">
|
||||||
|
<!-- 所属模块 -->
|
||||||
|
<div class="module-row">
|
||||||
|
<div class="case-title-wrap">
|
||||||
|
<div class="name title-wrap">
|
||||||
|
{{ $t("test_track.case.module") }}
|
||||||
|
</div>
|
||||||
|
<div class="required required-item"></div>
|
||||||
|
</div>
|
||||||
|
<case-diff-text :diffInfo="baseInfoDiffData.modules"></case-diff-text>
|
||||||
|
</div>
|
||||||
|
<!-- 自定义字段 -->
|
||||||
|
<div
|
||||||
|
class="module-row item-row"
|
||||||
|
v-for="(item, index) in customDiffData"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<div class="case-title-wrap">
|
||||||
|
<div class="name title-wrap">
|
||||||
|
{{ item.key }}
|
||||||
|
</div>
|
||||||
|
<div class="required required-item" v-if="item.required"></div>
|
||||||
|
</div>
|
||||||
|
<case-diff-text
|
||||||
|
:diffInfo="[{ diffArr: item.value }]"
|
||||||
|
></case-diff-text>
|
||||||
|
</div>
|
||||||
|
<!-- 关联需求 -->
|
||||||
|
<div class="module-row item-row">
|
||||||
|
<div class="case-title-wrap">
|
||||||
|
<div class="name title-wrap">
|
||||||
|
{{ $t("test_track.related_requirements") }}
|
||||||
|
</div>
|
||||||
|
<div class="required required-item"></div>
|
||||||
|
</div>
|
||||||
|
<case-diff-text :diffInfo="baseInfoDiffData.story"></case-diff-text>
|
||||||
|
</div>
|
||||||
|
<!-- 需求ID/名称 -->
|
||||||
|
<div class="module-row item-row">
|
||||||
|
<div class="case-title-wrap">
|
||||||
|
<div class="name title-wrap">
|
||||||
|
{{ $t("test_track.case.demand_name_id") }}
|
||||||
|
</div>
|
||||||
|
<div class="required required-item"></div>
|
||||||
|
</div>
|
||||||
|
<case-diff-text :diffInfo="baseInfoDiffData.storyId"></case-diff-text>
|
||||||
|
</div>
|
||||||
|
<!-- 标签 -->
|
||||||
|
<div class="module-row item-row">
|
||||||
|
<div class="case-title-wrap">
|
||||||
|
<div class="name title-wrap">
|
||||||
|
{{ $t("commons.tag") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<case-diff-text :diffInfo="tagDiffData.tags"></case-diff-text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import CaseDiffText from "./CaseDiffText";
|
||||||
|
import DefaultDiffExecutor from "./version_diff";
|
||||||
|
import CaseAttachmentViewer from "@/business/case/components/case/CaseAttachmentViewer";
|
||||||
|
import { getTestCaseVersions } from "@/api/testCase";
|
||||||
|
import { getCurrentProjectID } from "metersphere-frontend/src/utils/token";
|
||||||
|
import { getProjectVersions } from "metersphere-frontend/src/api/version";
|
||||||
|
import { attachmentList } from "@/api/attachment";
|
||||||
|
import { byteToSize } from "@/business/utils/sdk-utils";
|
||||||
|
import CaseDiffTestRelate from "./CaseDiffTestRelate";
|
||||||
|
import { getRelateTest } from "@/api/testCase";
|
||||||
|
import { testCaseCommentList } from "@/api/test-case-comment";
|
||||||
|
import CaseCommentViewer from "../CaseCommentViewer";
|
||||||
|
import CaseDiffRelationship from "./CaseDiffRelationship";
|
||||||
|
import CaseDiffIssueRelate from "./CaseDiffIssueRelate";
|
||||||
|
import { getRelationshipCase } from "@/api/testCase";
|
||||||
|
import { getIssuesByCaseIdWithSearch } from "@/api/issue";
|
||||||
|
import { getProjectMemberOption } from "metersphere-frontend/src/api/user";
|
||||||
|
import {
|
||||||
|
buildCustomFields,
|
||||||
|
buildTestCaseOldFields,
|
||||||
|
parseCustomField,
|
||||||
|
} from "metersphere-frontend/src/utils/custom_field";
|
||||||
|
import { getTestTemplate } from "@/api/custom-field-template";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "CaseDiffViewer",
|
||||||
|
components: {
|
||||||
|
CaseDiffText,
|
||||||
|
CaseAttachmentViewer,
|
||||||
|
CaseDiffTestRelate,
|
||||||
|
CaseCommentViewer,
|
||||||
|
CaseDiffRelationship,
|
||||||
|
CaseDiffIssueRelate,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
versionLeftId: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
versionRightId: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
caseId: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
translateX: 0,
|
||||||
|
//根据数据 每两个分为一组 由最后一组展示
|
||||||
|
currentGroupIndex: -1,
|
||||||
|
standardWith: 184,
|
||||||
|
prevBtn: true,
|
||||||
|
nextBtn: true,
|
||||||
|
//手动切换版本id
|
||||||
|
appointVersionRightId: "",
|
||||||
|
/**
|
||||||
|
* 正文
|
||||||
|
*/
|
||||||
|
caseActiveName: "detail",
|
||||||
|
defaultExecutor: {},
|
||||||
|
contentDiffData: {},
|
||||||
|
customDiffData: {},
|
||||||
|
attachmentDiffData: [],
|
||||||
|
baseInfoDiffData: {},
|
||||||
|
tagDiffData: {},
|
||||||
|
originCase: {},
|
||||||
|
targetCase: {},
|
||||||
|
// 版本id 与 具体的 case详情的关系
|
||||||
|
cacheVersionsMap: new Map(),
|
||||||
|
// 版本名称与版本id的对应关系
|
||||||
|
cacheVersionsNameMap: new Map(),
|
||||||
|
// 当前case 可用的版本id
|
||||||
|
caseVersionIds: new Set(),
|
||||||
|
// 版本列表
|
||||||
|
versionOptions: [],
|
||||||
|
versionList: [],
|
||||||
|
// 关联用例diff数据
|
||||||
|
testCaseRelateData: [],
|
||||||
|
// 评论diff数据
|
||||||
|
diffCommentsData: [],
|
||||||
|
// 前置
|
||||||
|
preTableData: [],
|
||||||
|
// 后置
|
||||||
|
postTableData: [],
|
||||||
|
//缺陷对比结果
|
||||||
|
issuesData: [],
|
||||||
|
//自定义字段
|
||||||
|
customFields: [],
|
||||||
|
testCaseTemplate: {},
|
||||||
|
memberOptions: [],
|
||||||
|
//自定义模板字段 缓存
|
||||||
|
cacheCustomFields: new Map(),
|
||||||
|
// 自定义字段 options - id 关系缓存
|
||||||
|
catchCustomOptions: new Map(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
let testTemplateRes = await getTestTemplate();
|
||||||
|
this.testCaseTemplate = testTemplateRes;
|
||||||
|
this.customFields = testTemplateRes.customFields || [];
|
||||||
|
this.refresh();
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
enablePrev() {
|
||||||
|
if (!this.versionList || this.versionList.length <= 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.prevBtn;
|
||||||
|
},
|
||||||
|
enableNext() {
|
||||||
|
if (!this.versionList || this.versionList.length <= 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.nextBtn;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 版本切换相关
|
||||||
|
*/
|
||||||
|
calculate() {
|
||||||
|
if (!this.versionList) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let length = this.versionList.length;
|
||||||
|
// length <= 2
|
||||||
|
if (length <= 2) {
|
||||||
|
this.prevBtn = false;
|
||||||
|
this.nextBtn = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//lenght > 2
|
||||||
|
this.currentGroupIndex = length;
|
||||||
|
//当前初始 从右往左
|
||||||
|
this.translateX = this.standardWith * (this.currentGroupIndex - 2) * -1;
|
||||||
|
this.nextBtn = false;
|
||||||
|
},
|
||||||
|
generateTranslateX(symbol = 1) {
|
||||||
|
this.translateX = this.standardWith * symbol + this.translateX;
|
||||||
|
},
|
||||||
|
prev() {
|
||||||
|
this.currentGroupIndex =
|
||||||
|
this.currentGroupIndex - 1 <= 0 ? 0 : this.currentGroupIndex - 1;
|
||||||
|
this.listenBtnStatus();
|
||||||
|
this.generateTranslateX();
|
||||||
|
},
|
||||||
|
next() {
|
||||||
|
this.currentGroupIndex =
|
||||||
|
this.currentGroupIndex + 1 >= this.versionList.length
|
||||||
|
? this.versionList.length
|
||||||
|
: this.currentGroupIndex + 1;
|
||||||
|
this.listenBtnStatus();
|
||||||
|
this.generateTranslateX(-1);
|
||||||
|
},
|
||||||
|
listenBtnStatus() {
|
||||||
|
this.prevBtn = this.currentGroupIndex > 2;
|
||||||
|
this.nextBtn = this.currentGroupIndex < this.versionList.length;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容对比
|
||||||
|
*/
|
||||||
|
tabClick() {},
|
||||||
|
async refresh() {
|
||||||
|
await this.fetchCaseVersions();
|
||||||
|
await this.fetchAllCaseVersion();
|
||||||
|
// 构造版本列表 根据顺序
|
||||||
|
this.formatVersionList();
|
||||||
|
this.checkoutVersionCase();
|
||||||
|
this.calculate();
|
||||||
|
this.defaultExecutor = new DefaultDiffExecutor(
|
||||||
|
this.originCase,
|
||||||
|
this.targetCase,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
this.defaultExecutor.diff();
|
||||||
|
|
||||||
|
// 自定义字段 填充value
|
||||||
|
if (this.customFields.length > 0) {
|
||||||
|
//获取成员列表 自定义字段 填充到类型为member、multipleMember的options中
|
||||||
|
let memberRes = await getProjectMemberOption();
|
||||||
|
this.memberOptions = memberRes.data || [];
|
||||||
|
}
|
||||||
|
this.fillCustomValue();
|
||||||
|
this.diffCustomData();
|
||||||
|
|
||||||
|
this.diffAttachment();
|
||||||
|
this.diffTestRelate();
|
||||||
|
this.diffComments();
|
||||||
|
this.diffRelationship();
|
||||||
|
this.diffIssues();
|
||||||
|
// 初始化
|
||||||
|
this.initContent();
|
||||||
|
},
|
||||||
|
initContent() {
|
||||||
|
this.contentDiffData = this.defaultExecutor.contentDiffData || {};
|
||||||
|
this.tagDiffData = this.defaultExecutor.tagDiffData || {};
|
||||||
|
this.baseInfoDiffData = this.defaultExecutor.baseInfoDiffData || {};
|
||||||
|
},
|
||||||
|
formatVersionList() {
|
||||||
|
let map = new Map();
|
||||||
|
this.caseVersionIds.forEach((v) => {
|
||||||
|
map.set(v, v);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.versionOptions.forEach((v) => {
|
||||||
|
if (map.get(v.id)) {
|
||||||
|
this.versionList.push(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async fetchAllCaseVersion() {
|
||||||
|
//首先获取所有版本,再去构造版本展示的数组
|
||||||
|
let res = await getProjectVersions(getCurrentProjectID());
|
||||||
|
this.versionOptions = res.data ?? [];
|
||||||
|
},
|
||||||
|
async fetchCaseVersions() {
|
||||||
|
let res = await getTestCaseVersions(this.caseId);
|
||||||
|
let data = res.data || [];
|
||||||
|
data.forEach((e) => {
|
||||||
|
this.cacheVersionsMap.set(e.versionId, e);
|
||||||
|
this.cacheVersionsNameMap.set(e.versionName, e.versionId);
|
||||||
|
this.caseVersionIds.add(e.versionId);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchAttachmentList(id) {
|
||||||
|
let data = { belongType: "testcase", belongId: id };
|
||||||
|
let tableData = [];
|
||||||
|
let response = await attachmentList(data);
|
||||||
|
let files = response.data;
|
||||||
|
if (!files) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tableData = JSON.parse(JSON.stringify(files));
|
||||||
|
tableData.map((f) => {
|
||||||
|
f.size = byteToSize(f.size);
|
||||||
|
f.status = "success";
|
||||||
|
f.progress = 100;
|
||||||
|
});
|
||||||
|
return tableData;
|
||||||
|
},
|
||||||
|
checkoutVersionCase() {
|
||||||
|
if (this.versionLeftId) {
|
||||||
|
this.originCase = this.cacheVersionsMap.get(this.versionLeftId);
|
||||||
|
}
|
||||||
|
if (this.versionRightId) {
|
||||||
|
this.targetCase = this.cacheVersionsMap.get(this.versionRightId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async diffAttachment() {
|
||||||
|
// 首先获取 两个版本的附件信息
|
||||||
|
let origin = await this.fetchAttachmentList(this.originCase.id);
|
||||||
|
let target = await this.fetchAttachmentList(this.targetCase.id);
|
||||||
|
this.attachmentDiffData = this.defaultExecutor.diffAttachment(
|
||||||
|
origin,
|
||||||
|
target
|
||||||
|
);
|
||||||
|
},
|
||||||
|
async fetchTestRelate(id) {
|
||||||
|
let res = await getRelateTest(id);
|
||||||
|
let data = res.data || [];
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
async diffTestRelate() {
|
||||||
|
let origin = await this.fetchTestRelate(this.originCase.id);
|
||||||
|
let target = await this.fetchTestRelate(this.targetCase.id);
|
||||||
|
this.testCaseRelateData = this.defaultExecutor.diffTableData(
|
||||||
|
origin,
|
||||||
|
target,
|
||||||
|
"num",
|
||||||
|
["projectName", "name", "testType"]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取评论信息
|
||||||
|
*/
|
||||||
|
async fetchComments(id) {
|
||||||
|
let res = await testCaseCommentList(id);
|
||||||
|
return res.data || [];
|
||||||
|
},
|
||||||
|
async diffComments() {
|
||||||
|
let origin = await this.fetchComments(this.originCase.id);
|
||||||
|
let target = await this.fetchComments(this.targetCase.id);
|
||||||
|
this.diffCommentsData = this.defaultExecutor.diffTableData(
|
||||||
|
origin,
|
||||||
|
target,
|
||||||
|
"id",
|
||||||
|
["description", "authorName"]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变更记录相关
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 依赖关系相关
|
||||||
|
*/
|
||||||
|
async fetchRelationshipData(id, type) {
|
||||||
|
let res = await getRelationshipCase(id, type);
|
||||||
|
return res.data || [];
|
||||||
|
},
|
||||||
|
|
||||||
|
async diffRelationship() {
|
||||||
|
// 前置 table数据
|
||||||
|
let origin = await this.fetchRelationshipData(this.originCase.id, "PRE");
|
||||||
|
let target = await this.fetchRelationshipData(this.targetCase.id, "PRE");
|
||||||
|
this.preTableData = this.defaultExecutor.diffTableData(
|
||||||
|
origin,
|
||||||
|
target,
|
||||||
|
"sourceId",
|
||||||
|
["targetName", "status"]
|
||||||
|
);
|
||||||
|
// 后置 table数据
|
||||||
|
let postOrigin = await this.fetchRelationshipData(
|
||||||
|
this.originCase.id,
|
||||||
|
"POST"
|
||||||
|
);
|
||||||
|
let postTarget = await this.fetchRelationshipData(
|
||||||
|
this.targetCase.id,
|
||||||
|
"POST"
|
||||||
|
);
|
||||||
|
this.postTableData = this.defaultExecutor.diffTableData(
|
||||||
|
postOrigin,
|
||||||
|
postTarget,
|
||||||
|
"sourceId",
|
||||||
|
["targetName", "status"]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关联缺陷
|
||||||
|
*/
|
||||||
|
async fetchIssues(id) {
|
||||||
|
let page = {
|
||||||
|
data: [],
|
||||||
|
result: {},
|
||||||
|
};
|
||||||
|
let condition = {};
|
||||||
|
let res = await getIssuesByCaseIdWithSearch(
|
||||||
|
"FUNCTIONAL",
|
||||||
|
id,
|
||||||
|
page,
|
||||||
|
condition
|
||||||
|
);
|
||||||
|
|
||||||
|
return page.data || [];
|
||||||
|
},
|
||||||
|
async diffIssues() {
|
||||||
|
let origin = await this.fetchIssues(this.originCase.id);
|
||||||
|
let target = await this.fetchIssues(this.targetCase.id);
|
||||||
|
this.issuesData = this.defaultExecutor.diffTableData(
|
||||||
|
origin,
|
||||||
|
target,
|
||||||
|
"id",
|
||||||
|
[
|
||||||
|
"title",
|
||||||
|
"description",
|
||||||
|
"platform",
|
||||||
|
"platformId",
|
||||||
|
"platformStatus",
|
||||||
|
"projectId",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
},
|
||||||
|
// fillCustomStruct(form) {
|
||||||
|
// return parseCustomField(
|
||||||
|
// form,
|
||||||
|
// this.testCaseTemplate,
|
||||||
|
// this.customFieldRules
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
fillCustomStruct(fields) {
|
||||||
|
if (!fields || fields.length <= 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
let temp = [];
|
||||||
|
fields.forEach((f) => {
|
||||||
|
let tempFiled = this.cacheCustomFields.get(f.id);
|
||||||
|
if (!tempFiled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 填充值
|
||||||
|
if (f.textValue) {
|
||||||
|
// 无需处理 options
|
||||||
|
tempFiled.lastValue = f.textValue;
|
||||||
|
temp.push(tempFiled);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!f.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 处理options
|
||||||
|
let res = f.value;
|
||||||
|
// 校验 转json 是否通过
|
||||||
|
try {
|
||||||
|
res = JSON.parse(f.value);
|
||||||
|
} catch {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断 options 是否存在
|
||||||
|
let options = this.catchCustomOptions.get(f.id);
|
||||||
|
if (!options || options.length <= 0) {
|
||||||
|
tempFiled.lastValue = res;
|
||||||
|
temp.push(tempFiled);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tempMap = new Map();
|
||||||
|
options.forEach((o) => {
|
||||||
|
if (o.value) {
|
||||||
|
tempMap.set(o.value, o);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//判断是否为数组
|
||||||
|
if (Array.isArray(res)) {
|
||||||
|
// 是数组 则
|
||||||
|
let translates = [];
|
||||||
|
res.forEach((r) => {
|
||||||
|
let option = tempMap.get(r);
|
||||||
|
if (option) {
|
||||||
|
translates.push(
|
||||||
|
option.system ? this.$t(option.text) : option.text
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tempFiled.lastValue = translates.join(" ");
|
||||||
|
temp.push(tempFiled);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let option = tempMap.get(res);
|
||||||
|
if (option) {
|
||||||
|
tempFiled.lastValue = option.system
|
||||||
|
? this.$t(option.text)
|
||||||
|
: option.text;
|
||||||
|
temp.push(tempFiled);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//将temp 转为 json
|
||||||
|
let resObj = {};
|
||||||
|
temp.forEach((t) => {
|
||||||
|
resObj[t.name] = t.lastValue || "";
|
||||||
|
});
|
||||||
|
return resObj;
|
||||||
|
},
|
||||||
|
diffCustomData() {
|
||||||
|
let filed1 = this.fillCustomStruct(this.originCase.fields);
|
||||||
|
let filed2 = this.fillCustomStruct(this.targetCase.fields);
|
||||||
|
this.customDiffData =
|
||||||
|
this.defaultExecutor.diffCustomData(filed1, filed2) || {};
|
||||||
|
},
|
||||||
|
fillCustomValue() {
|
||||||
|
// 缓存自定义字段信息
|
||||||
|
// 成员相关的 填充options
|
||||||
|
if (!this.customFields || this.customFields.length <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.customFields.forEach((c) => {
|
||||||
|
if (["member", "multipleMember"].indexOf(c.type) != -1) {
|
||||||
|
let standOptions = [];
|
||||||
|
this.memberOptions.forEach((mo) => {
|
||||||
|
let obj = {};
|
||||||
|
obj.system = false;
|
||||||
|
obj.text = mo.name;
|
||||||
|
obj.value = mo.id;
|
||||||
|
standOptions.push(obj);
|
||||||
|
});
|
||||||
|
c.options = standOptions;
|
||||||
|
}
|
||||||
|
if (c.options && c.options.length > 0) {
|
||||||
|
this.catchCustomOptions.set(c.id, c.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cacheCustomFields.set(c.id, c);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.diff-box {
|
||||||
|
background-color: #fff;
|
||||||
|
.switch-version-container {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 17px;
|
||||||
|
margin-left: 22px;
|
||||||
|
.prev-row {
|
||||||
|
width: 20px;
|
||||||
|
height: 64px;
|
||||||
|
background: #f5f6f7;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 64px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
i {
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.prev-row:hover {
|
||||||
|
background: rgba(31, 35, 41, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-viewer-row {
|
||||||
|
width: 368px;
|
||||||
|
overflow: hidden;
|
||||||
|
.tab-list {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
.version-item-row {
|
||||||
|
width: 180px;
|
||||||
|
min-width: 180px;
|
||||||
|
height: 64px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #f5f6f7;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-left: 4px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.version-label {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-top: 8px;
|
||||||
|
display: flex;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
.label {
|
||||||
|
color: #1f2329;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-new {
|
||||||
|
padding: 1px 6px;
|
||||||
|
height: 20px;
|
||||||
|
line-height: 20px;
|
||||||
|
background: rgba(120, 56, 135, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #783887;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-creator {
|
||||||
|
margin-left: 20px;
|
||||||
|
display: flex;
|
||||||
|
color: #646a73;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 22px;
|
||||||
|
.username {
|
||||||
|
}
|
||||||
|
|
||||||
|
.static-label {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.next-row {
|
||||||
|
width: 20px;
|
||||||
|
height: 64px;
|
||||||
|
background: #f5f6f7;
|
||||||
|
border-radius: 4px;
|
||||||
|
line-height: 64px;
|
||||||
|
margin-left: 4px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
i {
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.next-row:hover {
|
||||||
|
background: rgba(31, 35, 41, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.version-detail-diff-container {
|
||||||
|
width: 100%;
|
||||||
|
border-top: 1px solid rgba(31, 35, 41, 0.15);
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss">
|
||||||
|
.file-drawer .el-drawer__header {
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 公共样式
|
||||||
|
.line-through {
|
||||||
|
text-decoration-line: line-through;
|
||||||
|
color: #8f959e;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-row {
|
||||||
|
width: 32px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: rgba(52, 199, 36, 0.2);
|
||||||
|
padding: 0px 4px;
|
||||||
|
line-height: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #2ea121;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-row {
|
||||||
|
padding: 0px 4px;
|
||||||
|
width: 32px;
|
||||||
|
height: 16px;
|
||||||
|
background: rgba(245, 74, 69, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #d83931;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.format-change-row {
|
||||||
|
padding: 0px 4px;
|
||||||
|
width: 56px;
|
||||||
|
height: 16px;
|
||||||
|
background: rgba(255, 136, 0, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
color: #de7802;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
// 正文部分
|
||||||
|
@import "@/business/style/index.scss";
|
||||||
|
.content-body-wrap {
|
||||||
|
// 1024 减去左右padding 各24 和 1px右边框
|
||||||
|
width: px2rem(1024);
|
||||||
|
height: 1044px;
|
||||||
|
display: flex;
|
||||||
|
.tab-pane-wrap {
|
||||||
|
width: px2rem(896);
|
||||||
|
height: 100%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
border-right: 1px solid rgba(31, 35, 41, 0.15);
|
||||||
|
:deep(.el-tabs__item) {
|
||||||
|
padding-left: px2rem(24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.base-info-wrap {
|
||||||
|
width: px2rem(304);
|
||||||
|
height: calc(100vh - 130px);
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 24px;
|
||||||
|
.item-row {
|
||||||
|
margin-top: 21px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content-conatiner {
|
||||||
|
padding-left: px2rem(24);
|
||||||
|
padding-right: px2rem(24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.case-content-wrap {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
.case-title-wrap {
|
||||||
|
display: flex;
|
||||||
|
.title-wrap {
|
||||||
|
font-weight: 500;
|
||||||
|
height: 22px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
color: #1f2329;
|
||||||
|
}
|
||||||
|
margin-bottom: px2rem(8);
|
||||||
|
}
|
||||||
|
.required-item:after {
|
||||||
|
content: "*";
|
||||||
|
color: #f54a45;
|
||||||
|
margin-left: px2rem(4);
|
||||||
|
width: px2rem(8);
|
||||||
|
height: 22px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.attachment-preview :deep(.atta-box) {
|
||||||
|
width: 25rem !important;
|
||||||
|
}
|
||||||
|
:deep(.atta-box .atta-container) {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
:deep(.atta-box .atta-container .icon) {
|
||||||
|
// width: 100% !important;
|
||||||
|
}
|
||||||
|
:deep(.atta-box .atta-container .detail .filename) {
|
||||||
|
width: 60% !important;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,523 @@
|
||||||
|
/**
|
||||||
|
* 存储版本信息的数据结构
|
||||||
|
*/
|
||||||
|
class VersionData {
|
||||||
|
constructor({ diffArr }) {
|
||||||
|
this.diffArr = diffArr || [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比状态枚举
|
||||||
|
*/
|
||||||
|
class StatusType {
|
||||||
|
/**
|
||||||
|
* 无差异
|
||||||
|
*/
|
||||||
|
static NORMAL = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建
|
||||||
|
*/
|
||||||
|
static CREATE = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除
|
||||||
|
*/
|
||||||
|
static DELETE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式变化
|
||||||
|
*/
|
||||||
|
static FORMAT_CHANGE = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容格式组件枚举
|
||||||
|
*/
|
||||||
|
class ContentType {
|
||||||
|
/**
|
||||||
|
* 文本类型
|
||||||
|
*/
|
||||||
|
static TEXT = "TEXT";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数组类型
|
||||||
|
*/
|
||||||
|
static ARRAY = "ARRAY";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义字段
|
||||||
|
*/
|
||||||
|
static FIELD = "FIELD";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件对比
|
||||||
|
*/
|
||||||
|
static FILE = "FILE";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表格数据对比
|
||||||
|
*/
|
||||||
|
static TABLE = "TABLE";
|
||||||
|
}
|
||||||
|
|
||||||
|
class AbstractVersionDiffExecutor {
|
||||||
|
diff() {}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 版本对比执行器
|
||||||
|
*/
|
||||||
|
export default class DefaultDiffExecutor extends AbstractVersionDiffExecutor {
|
||||||
|
/**
|
||||||
|
* 构造器
|
||||||
|
* @param {*} origin 原始对象
|
||||||
|
* @param {*} target 对比对象
|
||||||
|
* @param {*} extra 扩展属性
|
||||||
|
*/
|
||||||
|
constructor(origin, target, extra = {}) {
|
||||||
|
super();
|
||||||
|
this.origin = origin || {};
|
||||||
|
this.target = target || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基础信息
|
||||||
|
* VersionData
|
||||||
|
*/
|
||||||
|
this.baseInfoDiffData = {};
|
||||||
|
|
||||||
|
this.tagDiffData = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详细信息
|
||||||
|
*/
|
||||||
|
this.contentDiffData = {};
|
||||||
|
/**
|
||||||
|
* 自定义信息
|
||||||
|
*/
|
||||||
|
this.customDiffData = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 附件信息对比
|
||||||
|
*/
|
||||||
|
this.attachmentDiffData = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
diff() {
|
||||||
|
// 处理 基础信息对比
|
||||||
|
//模块变更检测
|
||||||
|
this.baseInfoDiffData.modules = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffText(this.origin.nodePath, this.target.nodePath),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// 关联需求检测
|
||||||
|
this.baseInfoDiffData.story = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffText(this.origin.demandId, this.target.demandId),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// 需求id检测
|
||||||
|
this.baseInfoDiffData.storyId = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffText(
|
||||||
|
this.origin.demandName,
|
||||||
|
this.target.demandName
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
// 标签信息处理
|
||||||
|
this.tagDiffData.tags = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffArray(this.origin.tags, this.target.tags),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// // 自定义信息处理
|
||||||
|
// this.customDiffData = DiffUtil.diffCustomData(
|
||||||
|
// this.origin.customFieldForm,
|
||||||
|
// this.target.customFieldForm
|
||||||
|
// );
|
||||||
|
|
||||||
|
// 详细信息对比
|
||||||
|
//名称对比
|
||||||
|
this.contentDiffData.stepModel = "TEXT";
|
||||||
|
this.contentDiffData.caseName = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffText(this.origin.name, this.target.name),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
//前置条件对比
|
||||||
|
this.contentDiffData.prerequisite = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffText(
|
||||||
|
this.origin.prerequisite,
|
||||||
|
this.target.prerequisite,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
//文本描述
|
||||||
|
this.contentDiffData.stepDescription = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffText(
|
||||||
|
this.origin.stepDescription,
|
||||||
|
this.target.stepDescription,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
//预期结果
|
||||||
|
this.contentDiffData.expectedResult = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffText(
|
||||||
|
this.origin.expectedResult,
|
||||||
|
this.target.expectedResult,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
//备注
|
||||||
|
this.contentDiffData.remark = [
|
||||||
|
{
|
||||||
|
diffArr: DiffUtil.diffText(
|
||||||
|
this.origin.remark,
|
||||||
|
this.target.remark,
|
||||||
|
true
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// {"name":"open——2614e2dd-bcf9-4bb1-88ec-9737940ad7fc——1673837163926——screenshot.png","size":"0 B","updateTime":1675700468279,"progress":100,"status":"error","creator":"Administrator","type":"PNG","isLocal":true}
|
||||||
|
// this.attachmentDiffData.attachment = DiffUtil.diffAttachment(
|
||||||
|
// this.origin,
|
||||||
|
// this.target
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
|
diffAttachment(origin, target) {
|
||||||
|
this.attachmentDiffData.attachment = DiffUtil.diffAttachment(
|
||||||
|
origin,
|
||||||
|
target
|
||||||
|
);
|
||||||
|
return this.attachmentDiffData.attachment;
|
||||||
|
}
|
||||||
|
|
||||||
|
diffTableData(origin, target, key, props = []) {
|
||||||
|
return DiffUtil.diffTableData(origin, target, key, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
diffCustomData(fields1, fields2) {
|
||||||
|
// 自定义信息处理
|
||||||
|
this.customDiffData = DiffUtil.diffCustomData(fields1, fields2);
|
||||||
|
return this.customDiffData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DiffUtil {
|
||||||
|
static buildDiffData(status, content = "", type = ContentType.TEXT) {
|
||||||
|
let res = {};
|
||||||
|
res.status = status;
|
||||||
|
res.body = {
|
||||||
|
type: type,
|
||||||
|
content: content,
|
||||||
|
};
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 对比 文本内容
|
||||||
|
*/
|
||||||
|
static diffText(s1, s2, format = false) {
|
||||||
|
let resArr = [];
|
||||||
|
|
||||||
|
//统一空参数
|
||||||
|
if (s1 == "" || s1 == null || s1 == undefined) {
|
||||||
|
s1 = "";
|
||||||
|
}
|
||||||
|
if (s2 == "" || s2 == null || s2 == undefined) {
|
||||||
|
s2 = "";
|
||||||
|
}
|
||||||
|
// 无变化 -- s1===s2
|
||||||
|
if (s1 == s2) {
|
||||||
|
//s1 s2 均可
|
||||||
|
resArr.push(this.buildDiffData(StatusType.NORMAL, s1));
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增 -- s1不存在 s2存在
|
||||||
|
if (!s1 && s2) {
|
||||||
|
resArr.push(this.buildDiffData(StatusType.CREATE, s2));
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 -- s1存在 s2不存在
|
||||||
|
if (s1 && !s2) {
|
||||||
|
resArr.push(this.buildDiffData(StatusType.DELETE, s1));
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 都不为空
|
||||||
|
// 格式变化 -- s1、s2 均存在 且内容不一致
|
||||||
|
if (format) {
|
||||||
|
resArr.push(this.buildDiffData(StatusType.FORMAT_CHANGE, s2));
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 差异按照 新增、删除 进行标记
|
||||||
|
resArr.push(this.buildDiffData(StatusType.CREATE, s2));
|
||||||
|
resArr.push(this.buildDiffData(StatusType.DELETE, s1));
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比 数组
|
||||||
|
*
|
||||||
|
* 从数组总找出 新增和删除的
|
||||||
|
*/
|
||||||
|
static diffArray(arr1, arr2) {
|
||||||
|
let resArr = [];
|
||||||
|
//矫正参数
|
||||||
|
if (!Array.isArray(arr1)) {
|
||||||
|
arr1 = [];
|
||||||
|
}
|
||||||
|
if (!Array.isArray(arr2)) {
|
||||||
|
arr2 = [];
|
||||||
|
}
|
||||||
|
//返回原始数据
|
||||||
|
if ((!arr1 && !arr2) || arr1 == arr2) {
|
||||||
|
resArr.push(
|
||||||
|
this.buildDiffData(StatusType.NORMAL, arr1, ContentType.ARRAY)
|
||||||
|
);
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
let createArr = [];
|
||||||
|
let deleteArr = [];
|
||||||
|
let normalArr = [];
|
||||||
|
|
||||||
|
if (arr1.length <= 0 && arr2.length > 0) {
|
||||||
|
// arr2 全部为新增
|
||||||
|
createArr = arr2;
|
||||||
|
resArr.push(
|
||||||
|
this.buildDiffData(StatusType.CREATE, createArr, ContentType.ARRAY)
|
||||||
|
);
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arr1.length > 0 && arr2.length <= 0) {
|
||||||
|
//arr1 全部为删除
|
||||||
|
deleteArr = arr1;
|
||||||
|
resArr.push(
|
||||||
|
this.buildDiffData(StatusType.DELETE, deleteArr, ContentType.ARRAY)
|
||||||
|
);
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//以旧数组为基准 判断新数组 新增或删除的
|
||||||
|
for (let i = 0; i < arr2.length; i++) {
|
||||||
|
// 检测新增
|
||||||
|
let f1 = arr1.find((v) => v == arr2[i]);
|
||||||
|
if (!f1) {
|
||||||
|
createArr.push(arr2[i]);
|
||||||
|
} else {
|
||||||
|
normalArr.push(arr2[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < arr1.length; i++) {
|
||||||
|
// 检测删除
|
||||||
|
let f2 = arr2.find((v) => v == arr1[i]);
|
||||||
|
if (!f2) {
|
||||||
|
deleteArr.push(arr1[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (createArr.length > 0) {
|
||||||
|
resArr.push(
|
||||||
|
this.buildDiffData(StatusType.CREATE, createArr, ContentType.ARRAY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (deleteArr.length > 0) {
|
||||||
|
resArr.push(
|
||||||
|
this.buildDiffData(StatusType.DELETE, deleteArr, ContentType.ARRAY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (normalArr.length > 0) {
|
||||||
|
// resArr.push(
|
||||||
|
// this.buildDiffData(StatusType.NORMAL, normalArr, ContentType.ARRAY)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
|
//无差异返回原来的
|
||||||
|
if (createArr.length <= 0 && deleteArr.length <= 0) {
|
||||||
|
resArr.push(
|
||||||
|
this.buildDiffData(StatusType.NORMAL, arr2, ContentType.ARRAY)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比自定义字段信息
|
||||||
|
*/
|
||||||
|
static diffCustomData(fields1, fields2) {
|
||||||
|
let resArr = [];
|
||||||
|
|
||||||
|
if (!fields1) {
|
||||||
|
fields1 = {};
|
||||||
|
}
|
||||||
|
if (!fields2) {
|
||||||
|
fields2 = {};
|
||||||
|
}
|
||||||
|
if ((!fields1 && !fields2) || fields1 == fields2) {
|
||||||
|
// 无差异
|
||||||
|
Object.keys(fields2).forEach((e) => {
|
||||||
|
resArr.push({ key: e, value: this.diffText(fields2[e], fields2[e]) });
|
||||||
|
});
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fields1 不存在 fields2 存在 则fields2均为 新增字段
|
||||||
|
// fields1 存在 fields2 不存在 则fields1均为 删除字段
|
||||||
|
// 对比新增删除
|
||||||
|
|
||||||
|
Object.keys(fields2).forEach((e) => {
|
||||||
|
let findKey = Object.prototype.hasOwnProperty.call(fields1, e);
|
||||||
|
if (!findKey) {
|
||||||
|
resArr.push({
|
||||||
|
key: e,
|
||||||
|
value: this.diffText(undefined, fields2[e]),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//找到了 判断是否变更
|
||||||
|
let oldData = fields1[e];
|
||||||
|
let newData = fields2[e];
|
||||||
|
resArr.push({ key: e, value: this.diffText(oldData, newData) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比 文件
|
||||||
|
*/
|
||||||
|
static diffAttachment(origin, target) {
|
||||||
|
//矫正参数
|
||||||
|
if (!Array.isArray(origin)) {
|
||||||
|
origin = [];
|
||||||
|
}
|
||||||
|
if (!Array.isArray(target)) {
|
||||||
|
target = [];
|
||||||
|
}
|
||||||
|
let resArr = [];
|
||||||
|
let targetMap = new Map();
|
||||||
|
let originMap = new Map();
|
||||||
|
target.forEach((t) => {
|
||||||
|
targetMap.set(t.name, t);
|
||||||
|
});
|
||||||
|
originMap.forEach((o) => {
|
||||||
|
originMap.set(o.name, o);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 判读
|
||||||
|
target.forEach((t) => {
|
||||||
|
let o = originMap.get(t.name);
|
||||||
|
if (!o) {
|
||||||
|
//新增
|
||||||
|
t.diffStatus = StatusType.CREATE;
|
||||||
|
resArr.push(t);
|
||||||
|
} else {
|
||||||
|
//存在则对比 是否变更
|
||||||
|
if (
|
||||||
|
t.size !== o.size ||
|
||||||
|
t.progress !== o.progress ||
|
||||||
|
t.type !== o.type ||
|
||||||
|
t.creator !== o.creator ||
|
||||||
|
t.updateTime !== o.updateTime
|
||||||
|
) {
|
||||||
|
// 格式变化
|
||||||
|
t.diffStatus = StatusType.FORMAT_CHANGE;
|
||||||
|
resArr.push(t);
|
||||||
|
} else {
|
||||||
|
t.diffStatus = StatusType.NORMAL;
|
||||||
|
resArr.push(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
origin.forEach((o) => {
|
||||||
|
let t = targetMap.get(o.name);
|
||||||
|
if (!t) {
|
||||||
|
//标识已经删除
|
||||||
|
o.diffStatus = StatusType.DELETE;
|
||||||
|
resArr.push(o);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比表格数据
|
||||||
|
*/
|
||||||
|
static diffTableData(origin, target, key, props = []) {
|
||||||
|
//对比两个表格数组 并填充 diffStatus属性
|
||||||
|
if (!key) {
|
||||||
|
throw new Error("Diff key is undefined, please check it~");
|
||||||
|
}
|
||||||
|
//矫正参数
|
||||||
|
if (!Array.isArray(origin)) {
|
||||||
|
origin = [];
|
||||||
|
}
|
||||||
|
if (!Array.isArray(target)) {
|
||||||
|
target = [];
|
||||||
|
}
|
||||||
|
if (!Array.isArray(props)) {
|
||||||
|
props = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let resArr = [];
|
||||||
|
//首先 基于key 将数组转为map备用
|
||||||
|
let originMap = new Map();
|
||||||
|
let targetMap = new Map();
|
||||||
|
origin.forEach((o) => {
|
||||||
|
originMap.set(o[key], o);
|
||||||
|
});
|
||||||
|
target.forEach((t) => {
|
||||||
|
targetMap.set(t[key], t);
|
||||||
|
});
|
||||||
|
|
||||||
|
target.forEach((t) => {
|
||||||
|
//从原始数组中找是否存在,不存在则为新建状态
|
||||||
|
let o = originMap.get(t[key]);
|
||||||
|
if (!o) {
|
||||||
|
t.diffStatus = StatusType.CREATE;
|
||||||
|
resArr.push(t);
|
||||||
|
} else {
|
||||||
|
//存在则对比 props中的项目 查看差异
|
||||||
|
let factor = true;
|
||||||
|
for (let i = 0; i < props.length; i++) {
|
||||||
|
let p = props[i];
|
||||||
|
if (t[p] !== o[p]) {
|
||||||
|
factor = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.diffStatus = factor ? StatusType.NORMAL : StatusType.FORMAT_CHANGE;
|
||||||
|
resArr.push(t);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//逆向查找 找到已删除的
|
||||||
|
origin.forEach((o) => {
|
||||||
|
let t = targetMap.get(o[key]);
|
||||||
|
if (!t) {
|
||||||
|
o.diffStatus = StatusType.DELETE;
|
||||||
|
resArr.push(o);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return resArr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,6 +147,7 @@ import {
|
||||||
getProjectVersions,
|
getProjectVersions,
|
||||||
isProjectVersionEnable,
|
isProjectVersionEnable,
|
||||||
} from "metersphere-frontend/src/api/version";
|
} from "metersphere-frontend/src/api/version";
|
||||||
|
import { getTestCaseVersions } from "@/api/testCase";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "CaseVersionHistory",
|
name: "CaseVersionHistory",
|
||||||
|
@ -224,13 +225,27 @@ export default {
|
||||||
);
|
);
|
||||||
this.clearSelectData();
|
this.clearSelectData();
|
||||||
},
|
},
|
||||||
getVersionOptionList(callback) {
|
async getVersionOptionList(callback) {
|
||||||
getProjectVersions(this.currentProjectId).then((response) => {
|
// getProjectVersions(this.currentProjectId).then((response) => {
|
||||||
this.versionOptions = response.data.filter((v) => v.status === "open");
|
// this.versionOptions = response.data.filter((v) => v.status === "open");
|
||||||
if (callback) {
|
// if (callback) {
|
||||||
callback(this.versionOptions);
|
// callback(this.versionOptions);
|
||||||
}
|
// }
|
||||||
|
// });
|
||||||
|
let response = await getProjectVersions(this.currentProjectId);
|
||||||
|
let versions = response.data || [];
|
||||||
|
let getAllVersions = await getTestCaseVersions(this.currentId);
|
||||||
|
let allVersionCases = getAllVersions.data || [];
|
||||||
|
let tempMap = new Map();
|
||||||
|
allVersionCases.forEach((c) => {
|
||||||
|
tempMap.set(c.versionId, c);
|
||||||
});
|
});
|
||||||
|
this.versionOptions = versions.filter((v) => {
|
||||||
|
return tempMap.get(v.id);
|
||||||
|
});
|
||||||
|
if (callback) {
|
||||||
|
callback(this.versionOptions);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
updateUserDataByExternal() {
|
updateUserDataByExternal() {
|
||||||
if (this.testUsers && this.testUsers.length > 0) {
|
if (this.testUsers && this.testUsers.length > 0) {
|
||||||
|
@ -440,8 +455,9 @@ export default {
|
||||||
.icon {
|
.icon {
|
||||||
margin-left: 11.67px;
|
margin-left: 11.67px;
|
||||||
margin-right: 4.6px;
|
margin-right: 4.6px;
|
||||||
width: 14.67px;
|
margin-top: 3px;
|
||||||
height: 13.33px;
|
/* width: 14.67px;
|
||||||
|
height: 13.33px; */
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -126,7 +126,7 @@ export default {
|
||||||
return {
|
return {
|
||||||
isEmpty: false,
|
isEmpty: false,
|
||||||
imgUrl: "/assets/module/figma/icon_none.svg",
|
imgUrl: "/assets/module/figma/icon_none.svg",
|
||||||
label: "暂无数据",
|
label: this.$t("case.no_data"),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue