refactor(测试跟踪): 优化用例版本对比
This commit is contained in:
parent
d0b7823d1b
commit
4357374353
|
@ -445,12 +445,14 @@
|
|||
<!-- 底部操作按钮 -->
|
||||
<div class="edit-footer-container" v-if="editable">
|
||||
<template>
|
||||
<!-- 保存 -->
|
||||
<div
|
||||
class="save-btn-row"
|
||||
v-if="showAddBtn">
|
||||
<el-button size="small" @click="handleCommand" :disabled="readOnly">
|
||||
{{ $t("commons.save") }}
|
||||
<!-- 保存并新建 -->
|
||||
<div class="save-create-row">
|
||||
<el-button
|
||||
size="small"
|
||||
@click="handleCommand('ADD_AND_CREATE')"
|
||||
v-if="showAddBtn"
|
||||
:disabled="readOnly">
|
||||
{{ $t("case.saveAndCreate") }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 保存并添加到公共用例库 -->
|
||||
|
@ -462,20 +464,18 @@
|
|||
{{ $t("test_track.case.save_add_public") }}
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 保存并新建 -->
|
||||
<div class="save-create-row">
|
||||
<el-button
|
||||
size="small"
|
||||
@click="handleCommand('ADD_AND_CREATE')"
|
||||
v-if="showAddBtn"
|
||||
:disabled="readOnly">
|
||||
{{ $t("test_track.case.save_create_continue") }}
|
||||
<!-- 保存 -->
|
||||
<div
|
||||
class="save-btn-row"
|
||||
v-if="showAddBtn">
|
||||
<el-button size="small" @click="handleCommand" :disabled="readOnly" type="primary">
|
||||
{{ $t("commons.save") }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
<!-- <el-dialog
|
||||
:fullscreen="true"
|
||||
:visible.sync="dialogVisible"
|
||||
:destroy-on-close="true"
|
||||
|
@ -488,6 +488,9 @@
|
|||
:tree-nodes="treeNodes"
|
||||
></test-case-version-diff>
|
||||
</el-dialog>
|
||||
-->
|
||||
<!-- since v2.7 -->
|
||||
<case-diff-side-viewer ref="caseDiffViewerRef" ></case-diff-side-viewer>
|
||||
<version-create-other-info-select
|
||||
@confirmOtherInfo="confirmOtherInfo"
|
||||
ref="selectPropDialog"/>
|
||||
|
@ -577,7 +580,7 @@ import {buildTree} from "metersphere-frontend/src/model/NodeTree";
|
|||
import {versionEnableByProjectId} from "@/api/project";
|
||||
import {openCaseEdit} from "@/business/case/test-case";
|
||||
import ListItemDeleteConfirm from "metersphere-frontend/src/components/ListItemDeleteConfirm";
|
||||
|
||||
import CaseDiffSideViewer from "./case/diff/CaseDiffSideViewer"
|
||||
|
||||
export default {
|
||||
name: "TestCaseEdit",
|
||||
|
@ -610,7 +613,8 @@ export default {
|
|||
MsAsideContainer,
|
||||
MsMainContainer,
|
||||
MxVersionHistory,
|
||||
ListItemDeleteConfirm
|
||||
ListItemDeleteConfirm,
|
||||
CaseDiffSideViewer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -1564,27 +1568,31 @@ export default {
|
|||
}
|
||||
return versionName;
|
||||
},
|
||||
async compareBranch(t1, t2) {
|
||||
let t1Case = await testCaseGetByVersionId(t1.id, this.currentTestCaseInfo.id);
|
||||
let t2Case = await testCaseGetByVersionId(t2.id, this.currentTestCaseInfo.id);
|
||||
compareBranch(t1, t2) {
|
||||
// 打开对比
|
||||
this.dialogVisible = true;
|
||||
this.$refs.caseDiffViewerRef.open(t1.id, t2.id, this.currentTestCaseInfo.id)
|
||||
|
||||
let p1 = getTestCase(t1Case.data.id);
|
||||
let p2 = getTestCase(t2Case.data.id);
|
||||
let that = this;
|
||||
Promise.all([p1, p2]).then((r) => {
|
||||
if (r[0] && r[1]) {
|
||||
that.newData = r[0].data;
|
||||
that.oldData = r[1].data;
|
||||
that.newData.createTime = t1.createTime;
|
||||
that.oldData.createTime = t2.createTime;
|
||||
that.newData.versionName = t1.name;
|
||||
that.oldData.versionName = t2.name;
|
||||
that.newData.userName = t1Case.data.createName;
|
||||
that.oldData.userName = t2Case.data.createName;
|
||||
this.setSpecialPropForCompare(that);
|
||||
that.dialogVisible = true;
|
||||
}
|
||||
});
|
||||
// let t1Case = await testCaseGetByVersionId(t1.id, this.currentTestCaseInfo.id);
|
||||
// let t2Case = await testCaseGetByVersionId(t2.id, this.currentTestCaseInfo.id);
|
||||
|
||||
// let p1 = getTestCase(t1Case.data.id);
|
||||
// let p2 = getTestCase(t2Case.data.id);
|
||||
// let that = this;
|
||||
// Promise.all([p1, p2]).then((r) => {
|
||||
// if (r[0] && r[1]) {
|
||||
// that.newData = r[0].data;
|
||||
// that.oldData = r[1].data;
|
||||
// that.newData.createTime = t1.createTime;
|
||||
// that.oldData.createTime = t2.createTime;
|
||||
// that.newData.versionName = t1.name;
|
||||
// that.oldData.versionName = t2.name;
|
||||
// that.newData.userName = t1Case.data.createName;
|
||||
// that.oldData.userName = t2Case.data.createName;
|
||||
// this.setSpecialPropForCompare(that);
|
||||
// that.dialogVisible = true;
|
||||
// }
|
||||
// });
|
||||
},
|
||||
compare(row) {
|
||||
testCaseGetByVersionId(row.id, this.currentTestCaseInfo.refId).then(
|
||||
|
@ -2268,6 +2276,7 @@ export default {
|
|||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
justify-content: flex-end;
|
||||
// 底部按钮激活样式
|
||||
.opt-active-primary {
|
||||
background: #783887;
|
||||
|
@ -2287,7 +2296,7 @@ export default {
|
|||
}
|
||||
|
||||
.save-btn-row {
|
||||
margin-left: px2rem(24);
|
||||
margin: 0 24px 0 12px;
|
||||
el-button {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,18 @@
|
|||
<img :src="iconSrc" alt="" />
|
||||
</div>
|
||||
<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="size">{{ fileItem.size }}</div>
|
||||
<div class="split">|</div>
|
||||
|
@ -80,8 +91,12 @@
|
|||
<script>
|
||||
import {byteToSize, sizeToByte} from "@/business/utils/sdk-utils";
|
||||
|
||||
import CaseDiffStatus from "./diff/CaseDiffStatus";
|
||||
export default {
|
||||
name: "CaseAttachmentItem",
|
||||
components: {
|
||||
CaseDiffStatus,
|
||||
},
|
||||
props: {
|
||||
fileItem: Object,
|
||||
index: Number,
|
||||
|
@ -339,6 +354,10 @@ export default {
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
.filename {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.content {
|
||||
width: 100%;
|
||||
color: #1f2329;
|
||||
height: px2rem(22);
|
||||
|
@ -347,6 +366,7 @@ export default {
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.wait-upload {
|
||||
height: px2rem(20);
|
||||
font-size: 12px;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<div class="header-img-row">{{ getShortName(comment.authorName) }}</div>
|
||||
<div class="info">
|
||||
<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>
|
||||
|
||||
<template v-if="!readOnly">
|
||||
|
@ -13,15 +13,22 @@
|
|||
<div class="icon">
|
||||
<i class="el-icon-edit"></i>
|
||||
</div>
|
||||
<div class="label">{{$t('commons.edit')}}</div>
|
||||
<div class="label">{{ $t("commons.edit") }}</div>
|
||||
</div>
|
||||
<div class="remove opt-row" @click="deleteComment">
|
||||
<div class="icon">
|
||||
<i class="el-icon-delete"></i>
|
||||
</div>
|
||||
<div class="label">{{$t('commons.delete')}}</div>
|
||||
<div class="label">{{ $t("commons.delete") }}</div>
|
||||
</div>
|
||||
</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 class="viewer">
|
||||
|
@ -38,10 +45,12 @@
|
|||
import CaseCommentEdit from "./CaseCommentEdit";
|
||||
import { getCurrentUser } from "metersphere-frontend/src/utils/token";
|
||||
import { deleteMarkDownImgByName } from "@/business/utils/sdk-utils";
|
||||
import CaseDiffStatus from "./diff/CaseDiffStatus";
|
||||
export default {
|
||||
name: "CaseCommentViewItem",
|
||||
components: {
|
||||
CaseCommentEdit,
|
||||
CaseDiffStatus,
|
||||
},
|
||||
props: {
|
||||
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,
|
||||
isProjectVersionEnable,
|
||||
} from "metersphere-frontend/src/api/version";
|
||||
import { getTestCaseVersions } from "@/api/testCase";
|
||||
|
||||
export default {
|
||||
name: "CaseVersionHistory",
|
||||
|
@ -224,13 +225,27 @@ export default {
|
|||
);
|
||||
this.clearSelectData();
|
||||
},
|
||||
getVersionOptionList(callback) {
|
||||
getProjectVersions(this.currentProjectId).then((response) => {
|
||||
this.versionOptions = response.data.filter((v) => v.status === "open");
|
||||
async getVersionOptionList(callback) {
|
||||
// getProjectVersions(this.currentProjectId).then((response) => {
|
||||
// this.versionOptions = response.data.filter((v) => v.status === "open");
|
||||
// if (callback) {
|
||||
// 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() {
|
||||
if (this.testUsers && this.testUsers.length > 0) {
|
||||
|
@ -440,8 +455,9 @@ export default {
|
|||
.icon {
|
||||
margin-left: 11.67px;
|
||||
margin-right: 4.6px;
|
||||
width: 14.67px;
|
||||
height: 13.33px;
|
||||
margin-top: 3px;
|
||||
/* width: 14.67px;
|
||||
height: 13.33px; */
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
|
|
@ -126,7 +126,7 @@ export default {
|
|||
return {
|
||||
isEmpty: false,
|
||||
imgUrl: "/assets/module/figma/icon_none.svg",
|
||||
label: "暂无数据",
|
||||
label: this.$t("case.no_data"),
|
||||
};
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue