refactor(测试跟踪): 功能用例保存问题优化

This commit is contained in:
song-cc-rock 2023-03-02 18:33:18 +08:00 committed by fit2-zhao
parent 258c6147b3
commit 8000f871d3
9 changed files with 496 additions and 139 deletions

View File

@ -0,0 +1,283 @@
<template>
<span>
<el-select v-if="data.type === 'select' || data.type === 'multipleSelect'"
:loading="loading"
@click.native="clickPane"
:disabled="disabled"
:multiple="data.type === 'multipleSelect'"
:class="{'ms-select-tag' : data.type === 'multipleSelect'}"
@change="handleChange"
@clear="handleClear"
clearable
filterable
v-model="data[prop]"
:filter-method="data.inputSearch ? handleSelectInput : null"
:remote="data.inputSearch"
:placeholder="$t('commons.default')">
<el-option
v-for="(item,index) in data.options ? data.options : []"
:key="index"
@change="handleChange"
:label="getTranslateOption(item)"
:value="item.value">
</el-option>
</el-select>
<el-cascader
v-else-if="data.type === 'cascadingSelect'"
@click.native="clickPane"
expand-trigger="hover"
@change="handleChange"
:props="{label: 'text'}"
:options="data.options"
v-model="data[prop]">
</el-cascader>
<el-input
v-else-if="data.type === 'textarea'"
@click.native="clickPane"
type="textarea"
@change="handleChange"
:rows="2"
:disabled="disabled"
:placeholder="$t('commons.input_content')"
class="custom-with"
v-model="data[prop]">
</el-input>
<el-checkbox-group
v-else-if="data.type === 'checkbox'"
@click.native="clickPane"
:disabled="disabled"
v-model="data[prop]">
<el-checkbox v-for="(item, index) in data.options ? data.options : []"
:key="index"
@change="handleChange"
:label="item.value">
{{ getTranslateOption(item) }}
</el-checkbox>
</el-checkbox-group>
<el-radio
v-else-if="data.type === 'radio'"
@click.native="clickPane"
v-model="data[prop]"
:disabled="disabled"
v-for="(item,index) in data.options ? data.options : []"
:key="index"
@change="handleChange"
:label="item.value">
{{ getTranslateOption(item) }}
</el-radio>
<el-input-number
v-else-if="data.type === 'int'"
@click.native="clickPane"
v-model="data[prop]"
:disabled="disabled"
@change="handleChange"/>
<el-input-number
v-else-if="data.type === 'float'"
@click.native="clickPane"
:disabled="disabled"
@change="handleChange"
v-model="data[prop]" :precision="2" :step="0.1"/>
<el-date-picker
class="custom-with"
@click.native="clickPane"
@change="handleChange"
v-else-if="data.type === 'date' || data.type === 'datetime'"
:value-format="data.type === 'date' ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss'"
:disabled="disabled"
v-model="data[prop]"
:type="data.type === 'date' ? 'date' : 'datetime'"
:placeholder="$t('commons.select_date')">
</el-date-picker>
<el-select v-else-if="data.type === 'member' || data.type === 'multipleMember'"
@click.native="clickPane"
:multiple="data.type === 'multipleMember'"
:class="{'ms-select-tag' : data.type === 'multipleMember'}"
@change="handleChange"
clearable
:disabled="disabled"
filterable
v-model="data[prop]"
:placeholder="$t('commons.default')">
<el-option
v-for="(item) in memberOptions"
:key="item.id"
:label="item.name + (item.email ? ' (' + item.email + ')' : '')"
:value="item.id">
</el-option>
</el-select>
<ms-input-tag v-else-if="data.type === 'multipleInput'"
@click.native="clickPane"
@input="handleChange"
:read-only="disabled" :currentScenario="data" :prop="prop"/>
<ms-mark-down-text v-else-if="data.type === 'richText'"
@click.native="clickPane"
:prop="prop"
@change="handleChange"
:default-open="defaultOpen"
:data="data" :disabled="disabled"/>
<el-input v-else-if="data.type === 'password'"
@click.native="clickPane"
v-model="data[prop]"
class="custom-with"
auto-complete="new-password"
show-password
:disabled="disabled"
@input="handleChange"/>
<el-input v-else
@click.native="clickPane"
v-model="data[prop]"
class="custom-with"
maxlength="450"
show-word-limit
:disabled="disabled"
@input="handleChange"/>
</span>
</template>
<script>
import MsTableColumn from "../table/MsTableColumn";
import MsInputTag from "../MsInputTag";
import {getProjectMemberOption} from "../../api/user";
import MsMarkDownText from "metersphere-frontend/src/components/MsMarkDownText";
export default {
name: "CustomFiledComponent",
components: {MsMarkDownText, MsInputTag, MsTableColumn},
props: [
'data',
'prop',
'form',
'disabled',
'defaultOpen',
'isTemplateEdit'
],
data() {
return {
memberOptions: [],
originOptions: null,
loading: false
};
},
mounted() {
if (['select', 'multipleSelect', 'checkbox', 'radio'].indexOf(this.data.type) > -1 && this.data.options) {
let values = this.data[this.prop];
if (['multipleSelect', 'checkbox'].indexOf(this.data.type) > -1) {
if (values && values instanceof Array) {
for (let i = values.length - 1; i >= 0; i--) {
if (!this.data.options.find(item => item.value === values[i])) {
//
values.splice(i, 1);
}
}
} else {
//
this.data[this.prop] = [];
}
} else {
if (!this.data.options.find(item => item.value === values)) {
//
this.data[this.prop] = '';
}
}
}
this.setFormData();
if (['member', 'multipleMember'].indexOf(this.data.type) < 0) {
return;
}
getProjectMemberOption()
.then((r) => {
this.memberOptions = r.data;
if (this.data.name === '责任人' && this.data.system && this.isTemplateEdit) {
this.memberOptions.unshift({id: 'CURRENT_USER', name: '创建人', email: ''});
}
});
},
watch: {
form() {
this.setFormData();
}
},
methods: {
clickPane(){
this.$emit("onClick");
},
getTranslateOption(item) {
return item.system ? this.$t(item.text) : item.text;
},
handleChange() {
if (this.form) {
this.$set(this.form, this.data.name, this.data[this.prop]);
}
this.$emit('change', this.data.name);
this.$forceUpdate();
},
handleSelectInput(val) {
this.loading = true;
if (!this.originOptions) {
this.originOptions = this.data.options;
}
if (!val) {
//
this.data.options = this.originOptions;
}
this.$emit('inputSearch', this.data, val);
},
handleClear() {
if (this.originOptions && this.data.inputSearch) {
//
this.data.options = this.originOptions;
}
},
stopLoading() {
this.loading = false;
},
setFormData() {
if (this.form && this.data && this.data[this.prop]) {
this.$set(this.form, this.data.name, this.data[this.prop]);
}
}
}
};
</script>
<style scoped>
.el-select {
width: 100%;
}
.el-date-editor.el-input {
width: 100%;
}
.custom-with :deep( .el-input__inner) {
height: 32px;
}
:deep( .el-input--suffix .el-input__inner) {
height: 32px;
}
:deep(.ms-select-tag.el-select .el-tag__close.el-icon-close::before) {
position: relative;
top: -17px;
left: -6px;
}
:deep(.ms-select-tag.el-select span.el-select__tags-text) {
max-width: 240px!important;
}
</style>

View File

@ -264,15 +264,47 @@ i.el-dialog__close.el-icon.el-icon-close:before {
font-size: 17px;
}
/**
* el-icon-close
el-tag
*/
span.el-tag.el-tag--info.el-tag--mini.el-tag--light {
flex-direction: row;
align-items: center;
padding: 1px 6px;
gap: 4px;
height: 24px;
background: rgba(31, 35, 41, 0.1);
border-radius: 2px;
flex: none;
flex-grow: 0;
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
position: relative;
}
span.el-tag.el-tag--info.el-tag--mini.el-tag--light span{
display: inline-block;
max-width: 500px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: rgb(31, 35, 41);
}
/**
* el-tag-icon-close
*/
i.el-tag__close.el-icon-close {
height: 0;
}
.el-select .el-tag__close.el-icon-close::before {
font-size: 26px;
font-size: 28px;
position: relative;
top: -7px;
left: -6px;

View File

@ -56,6 +56,7 @@
</div>
</div>
<div class="header-opt-row" v-if="!editable">
<!-- 公共用例库头部按钮展示 -->
<div
class="previous-public-row head-opt"
:class="{'disable-row': isFirstPublic}"
@ -81,6 +82,46 @@
<div v-if="isPublicShow">
<span class="separator-row">|</span>
</div>
<div
class="edit-public-row head-opt"
v-if="isPublicShow"
@click="editPublicCase"
>
<div class="icon-row">
<img src="/assets/module/figma/icon_edit_outlined.svg" alt="" />
</div>
<div class="label-row">{{ $t("commons.edit") }}</div>
</div>
<div
class="copy-public-row head-opt"
v-if="isPublicShow"
@click="copyPublicCase"
>
<div class="icon-row">
<img src="/assets/module/figma/icon_copy_outlined.svg" alt="" />
</div>
<div class="label-row">{{ $t("commons.copy") }}</div>
</div>
<div v-if="isPublicShow">
<span class="separator-row">|</span>
</div>
<div
class="close-row head-opt"
v-if="isPublicShow"
@click="closePublicCase"
>
<span class="el-icon-close"></span>
</div>
<!-- 功能用例库头部按钮展示 -->
<div
class="follow-row head-opt" v-if="!isPublicShow"
@click="toEdit"
>
<div class="icon-row">
<img src="/assets/module/figma/icon_edit_outlined.svg" alt="" />
</div>
<div class="label-row">{{ $t("commons.edit") }}</div>
</div>
<div
class="follow-row head-opt"
v-if="!showFollow && !isPublicShow"
@ -101,25 +142,17 @@
</div>
<div class="label-row">{{ $t("case.followed") }}</div>
</div>
<div
class="follow-row head-opt" v-if="!isPublicShow"
@click="toEdit"
>
<div class="icon-row">
<img src="/assets/module/figma/icon_edit_outlined.svg" alt="" />
</div>
<div class="label-row">{{ $t("commons.edit") }}</div>
</div>
<div class="more-row head-opt" v-if="!isPublicShow">
<div class="icon-row">
<div class="icon-row" @mouseenter="$refs.headMoreOptPopover.doShow()" @mouseleave="$refs.headMoreOptPopover.doClose()">
<img src="/assets/module/figma/icon_more_outlined.svg" alt="" />
</div>
<div class="label-row">
<el-popover
placement="bottom-start"
placement="bottom-end"
trigger="hover"
popper-class="case-step-item-popover"
popper-class="more-opt-item-popover"
:visible-arrow="false"
ref="headMoreOptPopover"
>
<div class="opt-row">
<div
@ -161,36 +194,6 @@
</el-popover>
</div>
</div>
<div
class="edit-public-row head-opt"
v-if="isPublicShow"
@click="editPublicCase"
>
<div class="icon-row">
<img src="/assets/module/figma/icon_edit_outlined.svg" alt="" />
</div>
<div class="label-row">{{ $t("commons.edit") }}</div>
</div>
<div
class="copy-public-row head-opt"
v-if="isPublicShow"
@click="copyPublicCase"
>
<div class="icon-row">
<img src="/assets/module/figma/icon_copy_outlined.svg" alt="" />
</div>
<div class="label-row">{{ $t("commons.copy") }}</div>
</div>
<div v-if="isPublicShow">
<span class="separator-row">|</span>
</div>
<div
class="close-row head-opt"
v-if="isPublicShow"
@click="closePublicCase"
>
<span class="el-icon-close"></span>
</div>
</div>
</div>
<!-- 检测版本 是否不是最新 -->
@ -232,6 +235,7 @@
<el-scrollbar>
<case-base-info
:editable="editable"
:editable-state="editableState"
:case-id="form.id"
:project-id="projectId"
:form="form"
@ -1222,12 +1226,14 @@ export default {
let param = this.buildParam();
if (this.validate(param)) {
let option = this.getOption(param);
if (!isAddPublic) {
this.loading = true;
}
this.$request(option)
.then((response) => {
if (this.editableState) {
this.editableState = false;
this.$refs.otherInfo.caseActiveName = 'detail';
// , reload
location.reload();
}
response = response.data;
//
@ -1464,9 +1470,7 @@ export default {
break;
}
}
this.loading = true;
testCaseEditFollows(this.form.id, this.form.follows).then(() => {
this.loading = false;
this.$success(this.$t("commons.cancel_follow_success"), false);
});
} else {
@ -1476,9 +1480,7 @@ export default {
}
this.form.follows.push(this.currentUser().id);
this.loading = true;
testCaseEditFollows(this.form.id, this.form.follows).then(() => {
this.loading = false;
this.$success(this.$t("commons.follow_success"), false);
});
}
@ -2266,24 +2268,86 @@ export default {
min-width: 120px !important;
}
.case-step-item-popover .sub-opt-row .icon img {
width: 14px;
height: 14px;
.more-opt-item-popover .sub-opt-row .icon img {
width: 15px;
height: 15px;
}
.case-step-item-popover .add-public-row .icon {
.more-opt-item-popover .add-public-row .icon {
color: #646a73;
margin-top: 3px;
margin-top: 4px;
}
.case-step-item-popover .add-public-row .title {
.more-opt-item-popover .add-public-row .title {
color: #1f2329;
margin-right: 10px;
}
.case-step-item-popover .add-public-row:hover {
.more-opt-item-popover .add-public-row:hover {
background-color: rgba(31, 35, 41, 0.1);
}
.case-step-item-popover .split {
width: 140px;
.more-opt-item-popover {
padding: 0 !important;
min-width: 118px !important;
}
.more-opt-item-popover .opt-row {
display: flex;
flex-direction: column;
align-items: center;
}
.more-opt-item-popover .sub-opt-row {
display: flex;
width: 100%;
height: 32px;
margin: 3px 0 4px;
line-height: 32px;
cursor: pointer;
}
.more-opt-item-popover .sub-opt-row .icon {
margin-left: 13px;
margin-right: 9.33px;
}
.more-opt-item-popover .sub-opt-row .title {
font-family: "PingFang SC";
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 22px;
/* identical to box height, or 157% */
display: flex;
align-items: center;
letter-spacing: -0.1px;
}
.more-opt-item-popover .split {
width: 170px;
height: 1px;
background-color: rgba(31, 35, 41, 0.15);
}
.more-opt-item-popover .copy-row:hover {
background-color: rgba(31, 35, 41, 0.1);;
}
.more-opt-item-popover .copy-row .icon {
color: #646a73;
}
.more-opt-item-popover .copy-row .title {
color: #1f2329;
}
.more-opt-item-popover .delete-row:hover {
background-color: rgba(31, 35, 41, 0.1)!important;
}
.more-opt-item-popover .delete-row .icon {
color: #f54a45;
}
.more-opt-item-popover .delete-row .title {
color: #f54a45;
}
.more-opt-item-popover .delete-row {
background-color: transparent;
padding: 0;
}
</style>

View File

@ -1,7 +1,44 @@
<template>
<!-- 功能用例编辑页面右侧基本信息-->
<div v-loading="isloading" class="case-base-info-form">
<el-form ref="caseFrom" :rules="headerRules" :model="form" class="case-padding">
<!-- ID及自定义ID -->
<div class="id-row case-wrap" v-if="!editable || isCustomNum">
<div class="case-title-wrap">
<div class="name title-wrap">ID</div>
<div class="required required-item"></div>
</div>
<div class="side-content">
<base-edit-item-component
:editable="editable"
:auto-save="!readOnly"
trigger="hover"
:contentObject="{
content: isCustomNum ? form.customNum : form.num,
contentType: 'INPUT',
}"
:readonlyHoverEvent="!readOnly"
:content-click-event="!readOnly"
:model="form"
:rules="rules"
>
<template v-slot:content="{ onClick, hoverEditable }">
<div :class="hoverEditable ? 'selectHover' : ''">
<el-form-item prop="customNum">
<el-input
:disabled="readOnly"
v-model.trim="form.customNum"
size="small"
class="ms-case-input"
@click.native="onClick"
></el-input>
</el-form-item>
</div>
</template>
</base-edit-item-component>
</div>
</div>
<!-- 公共用例展示所属项目 -->
<div class="module-row case-wrap" v-if="publicEnable">
<div class="case-title-wrap">
<div class="name title-wrap">
@ -46,8 +83,7 @@
</base-edit-item-component>
</div>
</div>
<!-- 用例库展示项目不展示模块 -->
<!-- 用例展示所属模块 -->
<div class="module-row case-wrap" v-else>
<div class="case-title-wrap">
<div class="name title-wrap">
@ -92,9 +128,8 @@
</base-edit-item-component>
</div>
</div>
</el-form>
<!-- 自定义字段 -->
<!-- 自定义字段Form -->
<el-form
v-if="isFormAlive"
:model="customFieldForm"
@ -115,6 +150,7 @@
/>
</el-form>
<el-form ref="baseCaseFrom" :rules="rules" :model="form" class="case-padding">
<!-- 版本字段 -->
<div class="version-row case-wrap" v-if="versionEnable && !this.form.id">
<div class="case-title-wrap">
<div class="name title-wrap">{{ $t("commons.version") }}</div>
@ -159,41 +195,7 @@
</base-edit-item-component>
</div>
</div>
<div class="id-row case-wrap" v-if="!editable || isCustomNum">
<div class="case-title-wrap">
<div class="name title-wrap">ID</div>
<div class="required required-item"></div>
</div>
<div class="side-content">
<base-edit-item-component
:editable="editable"
:auto-save="isCustomNum"
trigger="hover"
:contentObject="{
content: isCustomNum ? form.customNum : form.num,
contentType: 'INPUT',
}"
:readonlyHoverEvent="isCustomNum && !readOnly"
:content-click-event="isCustomNum && !readOnly"
:model="form"
:rules="rules"
>
<template v-slot:content="{ onClick, hoverEditable }">
<div :class="hoverEditable ? 'selectHover' : ''">
<el-form-item prop="customNum">
<el-input
:disabled="readOnly"
v-model.trim="form.customNum"
size="small"
class="ms-case-input"
@click.native="onClick"
></el-input>
</el-form-item>
</div>
</template>
</base-edit-item-component>
</div>
</div>
<!-- 关联需求 -->
<div class="story-row case-wrap">
<div class="case-title-wrap">
<div class="name title-wrap">
@ -253,7 +255,7 @@
</base-edit-item-component>
</div>
</div>
<!-- 选择了关联需求后展示并且必填 -->
<!-- 需求名称: 选择了关联需求后展示并且必填 -->
<div class="story-name-row case-wrap" v-if="form.demandId === 'other'">
<div class="case-title-wrap">
<div class="name title-wrap">
@ -291,6 +293,7 @@
</base-edit-item-component>
</div>
</div>
<!-- 标签字段 -->
<div class="tag-row case-wrap">
<div class="case-title-wrap">
<div class="name title-wrap">{{ $t("commons.tag") }}</div>
@ -448,6 +451,7 @@ export default {
},
props: {
editable: Boolean,
editableState: Boolean,
form: Object,
isFormAlive: Boolean,
isloading: Boolean,
@ -468,7 +472,7 @@ export default {
return useStore().currentProjectIsCustomNum;
},
defaultModuleKey() {
if (this.editable) {
if (this.editable && !this.editableState) {
let defaultNodeKey = '';
if (this.$route.query.createNodeId) {
defaultNodeKey = this.$route.query.createNodeId;

View File

@ -27,7 +27,7 @@
<template v-slot:content="{ onClick, hoverEditable }">
<div :class="hoverEditable ? 'selectHover' : ''">
<el-form-item :prop="item.name">
<custom-filed-component
<ms-custom-filed-component
:data="item"
:form="form"
prop="defaultValue"
@ -51,13 +51,13 @@
<script>
import { SYSTEM_FIELD_NAME_MAP } from "metersphere-frontend/src/utils/table-constants";
import CustomFiledComponent from "metersphere-frontend/src/components/template/CustomFiledComponent";
import MsCustomFiledComponent from "metersphere-frontend/src/components/new-ui/MsCustomFiledComponent";
import { sortCustomFields } from "metersphere-frontend/src/utils/custom_field";
import BaseEditItemComponent from "../BaseEditItemComponent";
export default {
name: "CaseCustomFiledFormRow",
components: { CustomFiledComponent, BaseEditItemComponent },
components: { MsCustomFiledComponent, BaseEditItemComponent },
props: {
rules: Object,
editable: Boolean,

View File

@ -3,7 +3,7 @@
<!-- 基于form组件进行表单验证 -->
<el-form ref="caseDetailForm" :rules="rules" :model="form">
<!-- case name -->
<div class="case-name-row" v-on:keydown.enter.prevent>
<div class="case-name-row" v-on:keydown.enter.prevent v-if="editableState">
<div class="case-name case-title-wrap">
<div class="name title-wrap">{{ $t("case.case_name") }}</div>
<div class="required required-item"></div>

View File

@ -13,6 +13,7 @@
<case-detail-component
:type="type"
:case-id="caseId"
:editable-state="editableState"
:read-only="readOnly || !editable"
:project-id="projectId"
:is-copy="isCopy"

View File

@ -91,32 +91,6 @@
</div>
<i slot="reference" class="el-icon-more"></i>
</el-popover>
<!-- <el-button
type="primary"
:disabled="readOnly"
icon="el-icon-plus"
circle
size="mini"
@click="handleAddStep(scope.$index, scope.row)"
></el-button>
<el-button
icon="el-icon-document-copy"
type="success"
:disabled="readOnly"
circle
size="mini"
@click="handleCopyStep(scope.$index, scope.row)"
></el-button>
<el-button
type="danger"
icon="el-icon-delete"
circle
size="mini"
@click="handleDeleteStep(scope.$index, scope.row)"
:disabled="
readOnly || (scope.$index == 0 && form.steps.length <= 1)
"
></el-button> -->
</template>
</el-table-column>
</el-table>
@ -311,10 +285,10 @@ export default {
height: 8px;
}
.table-edit-input :deep(.el-textarea__inner:hover) {
border-style: solid!important;
border-color: #783887!important;
}
/*.table-edit-input :deep(.el-textarea__inner:hover) {*/
/* border-style: solid!important;*/
/* border-color: #783887!important;*/
/*}*/
.table-edit-input :deep(.el-textarea__inner:focus) {
border-style: solid!important;
@ -352,8 +326,7 @@ i.el-icon-more:hover {
display: flex;
width: 100%;
height: 32px;
margin-top: 4px;
margin-bottom: 4px;
margin: 4px 0 4px;
line-height: 32px;
cursor: pointer;
}

View File

@ -92,7 +92,7 @@ export default {
}
.case-name {
height: px2rem(24);
height: auto;
font-size: 16px;
font-family: "PingFang SC";
font-style: normal;