feat(接口测试): HTTP部分参数列支持可配置

HTTP部分参数列支持可配置
This commit is contained in:
song-tianyang 2022-11-30 17:55:38 +08:00 committed by 建国
parent 677948fe7e
commit 1af9a3d173
14 changed files with 1048 additions and 395 deletions

View File

@ -2,25 +2,38 @@
<div id="app" v-loading="loading">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane :label="$t('organization.message.template')" name="apiTemplate">
<el-button
v-show="!jsonSchemaDisable"
type="primary"
size="mini"
style="margin: 10px 10px 0px"
@click="openOneClickOperation">
{{ this.$t('commons.import') }}
</el-button>
<div>
<el-button
v-show="!jsonSchemaDisable"
type="primary"
size="mini"
style="margin: 10px 10px 0px"
@click="openOneClickOperation">
{{ this.$t('commons.import') }}
</el-button>
<div style="float: right">
<api-params-config
v-if="apiJsonSchemaConfigFields"
:storage-key="storageKey"
@refresh="refreshApiParamsField"
:api-params-config-fields="apiJsonSchemaConfigFields" />
</div>
</div>
<div :style="jsonSchemaDisable ? '' : 'min-height: 200px'">
<json-schema-editor
class="schema"
:disabled="jsonSchemaDisable"
:value="schema"
:show-mock-vars="showMockVars"
:scenario-definition="scenarioDefinition"
@editScenarioAdvance="editScenarioAdvance"
:need-mock="needMock"
lang="zh_CN"
custom />
<div style="overflow: auto">
<json-schema-editor
v-if="reloadedApiVariable"
class="schema"
:disabled="jsonSchemaDisable"
:value="schema"
:show-mock-vars="showMockVars"
:scenario-definition="scenarioDefinition"
@editScenarioAdvance="editScenarioAdvance"
:param-columns="apiJsonSchemaShowColumns"
:need-mock="needMock"
lang="zh_CN"
custom />
</div>
</div>
</el-tab-pane>
<el-tab-pane v-if="showPreview" :label="$t('schema.preview')" name="preview">
@ -37,13 +50,15 @@
<script>
import MsImportJson from './import/ImportJson';
import JsonSchemaEditor from '@/business/commons/json-schema/schema/editor/index';
import { getApiJsonSchemaConfigFields, getShowFields } from 'metersphere-frontend/src/utils/custom_field';
import ApiParamsConfig from '@/business/definition/components/request/components/ApiParamsConfig';
const Convert = require('./convert/convert.js');
const MsConvert = new Convert();
export default {
name: 'App',
components: { MsImportJson, JsonSchemaEditor },
components: { MsImportJson, JsonSchemaEditor, ApiParamsConfig },
props: {
body: {},
showPreview: {
@ -76,6 +91,7 @@ export default {
this.schema = { root: this.body.jsonSchema };
}
this.body.jsonSchema = this.schema.root;
this.apiJsonSchemaShowColumns = getShowFields(this.storageKey);
},
watch: {
schema: {
@ -106,11 +122,18 @@ export default {
},
},
loading: false,
reloadedApiVariable: true,
preview: null,
activeName: 'apiTemplate',
storageKey: 'API_JSON_SCHEMA_SHOW_FIELD',
apiJsonSchemaConfigFields: getApiJsonSchemaConfigFields(this),
apiJsonSchemaShowColumns: [],
};
},
methods: {
refreshApiParamsField() {
this.apiJsonSchemaShowColumns = getShowFields(this.storageKey);
},
handleClick() {
if (this.activeName === 'preview') {
this.loading = true;

View File

@ -1,7 +1,7 @@
<template>
<div class="json-schema-editor">
<el-row class="row" :gutter="20">
<el-col :span="8" class="ms-col-name">
<div class="json-schema-editor" style="padding: 0 10px">
<el-row class="row" :gutter="10">
<el-col :style="{ minWidth: `${200 - 10 * deep}px` }" class="ms-col-name">
<div :style="{ marginLeft: `${10 * deep}px` }" class="ms-col-name-c" />
<span
v-if="pickValue.type === 'object' || pickValue.type === 'array'"
@ -32,17 +32,19 @@
@change="onCheck" />
</el-tooltip>
</el-col>
<el-col :span="4">
<el-col style="width: 120px; padding: 0 5px">
<el-select
v-model="pickValue.type"
:disabled="disabled || disabledType"
class="ms-col-type"
@change="onChangeType"
style="width: 110px"
size="small">
<el-option :key="t" :value="t" :label="t" v-for="t in types" />
</el-select>
</el-col>
<el-col :span="4">
<el-col style="min-width: 200px; padding: 0 5px">
<ms-mock
:disabled="
disabled ||
@ -55,9 +57,103 @@
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
:need-mock="needMock"
style="width: 100%"
@editScenarioAdvance="editScenarioAdvance" />
</el-col>
<el-col :span="4">
<el-col v-if="showColumns('MIX_LENGTH')" class="item kv-select" style="width: 150px; padding: 0 5px">
<el-input-number
:min="0"
v-model="pickValue.minLength"
:placeholder="$t('schema.minLength')"
size="small"
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
style="width: 140px" />
</el-col>
<el-col v-if="showColumns('MAX_LENGTH')" class="item kv-select" style="width: 150px; padding: 0 5px">
<el-input-number
:min="0"
v-model="pickValue.maxLength"
:placeholder="$t('schema.maxLength')"
size="small"
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
style="width: 140px" />
</el-col>
<el-col v-if="showColumns('DEFAULT')" class="item kv-select" style="min-width: 200px; padding: 0 5px">
<el-input
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
v-model="pickValue.default"
class="ms-col-title"
:placeholder="$t('schema.default')"
style="width: 100%"
size="small" />
</el-col>
<el-col v-if="showColumns('PATTERN')" style="min-width: 200px; padding: 0 5px">
<el-input
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
v-model="pickValue.pattern"
class="ms-col-title"
:placeholder="$t('schema.pattern')"
size="small" />
</el-col>
<el-col v-if="showColumns('FORMAT')" style="min-width: 120px; padding: 0 5px">
<div v-if="advancedAttr.format">
<el-select
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
v-model="pickValue.format"
style="width: 100%"
size="small">
<el-option value="" :label="$t('schema.nothing')"></el-option>
<el-option :key="t" :value="t" :label="t" v-for="t in advancedAttr.format.enums" />
</el-select>
</div>
<div v-else>
<el-input :disabled="true" size="small" :placeholder="$t('schema.format')"></el-input>
</div>
</el-col>
<el-col v-if="showColumns('ENUM')" style="min-width: 300px; padding: 0 5px">
<el-input
type="textarea"
autosize
:disabled="disabled"
v-model="pickValue.enum"
class="ms-col-title"
:placeholder="$t('schema.enum')"
size="small" />
</el-col>
<el-col v-if="showColumns('DESCRIPTION')" style="min-width: 300px; padding: 0 5px">
<el-input
:disabled="disabled"
v-model="pickValue.description"
@ -65,16 +161,19 @@
:placeholder="$t('schema.description')"
size="small" />
</el-col>
<el-col :span="4" class="col-item-setting" v-if="!disabled">
<el-tooltip class="item" effect="dark" :content="$t('schema.adv_setting')" placement="top">
<i class="el-icon-setting" @click="onSetting" />
</el-tooltip>
<el-tooltip v-if="isObject || isArray(pickValue)" :content="$t('schema.add_child_node')" placement="top">
<i class="el-icon-plus" @click="addChild" style="margin-left: 10px" />
</el-tooltip>
<el-tooltip v-if="!root && !isItem" :content="$t('schema.remove_node')" placement="top">
<i class="el-icon-close" @click="removeNode" style="margin-left: 10px" />
</el-tooltip>
<!--其余操作-->
<el-col style="width: 220px" class="col-item-setting" v-if="!disabled">
<div style="width: 80px">
<el-tooltip class="item" effect="dark" :content="$t('schema.adv_setting')" placement="top">
<i class="el-icon-setting" @click="onSetting" />
</el-tooltip>
<el-tooltip v-if="isObject || isArray(pickValue)" :content="$t('schema.add_child_node')" placement="top">
<i class="el-icon-plus" @click="addChild" style="margin-left: 10px" />
</el-tooltip>
<el-tooltip v-if="!root && !isItem" :content="$t('schema.remove_node')" placement="top">
<i class="el-icon-close" @click="removeNode" style="margin-left: 10px" />
</el-tooltip>
</div>
</el-col>
</el-row>
@ -87,6 +186,7 @@
:deep="deep + 1"
:root="false"
class="children"
:param-columns="paramColumns"
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
:disabled="disabled"
@ -106,6 +206,7 @@
:deep="deep + 1"
:root="false"
class="children"
:param-columns="paramColumns"
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
:disabled="disabled"
@ -188,6 +289,7 @@ export default {
type: Object,
required: true,
},
paramColumns: Array,
showMockVars: {
type: Boolean,
default() {
@ -302,6 +404,12 @@ export default {
}
},
methods: {
showColumns(columns) {
if (!this.paramColumns) {
return false;
}
return this.paramColumns.indexOf(columns) >= 0;
},
isArray(data) {
let isDataArray = data.type === 'array';
if (isDataArray) {

View File

@ -3,6 +3,7 @@
<el-autocomplete
size="small"
class="input-with-autocomplete"
style="width: 100%"
v-model="mock.mock"
:fetch-suggestions="funcSearch"
:disabled="disabled"

View File

@ -1,107 +1,175 @@
<template>
<div style="margin-bottom: 20px">
<span class="kv-description" v-if="description">
{{ description }}
</span>
<el-row>
<el-checkbox v-model="isSelectAll" v-if="parameters.length > 1" />
</el-row>
<div class="item kv-row" v-for="(item, index) in parameters" :key="index">
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
<el-col class="kv-checkbox" v-if="isShowEnable">
<el-checkbox v-if="!isDisable(index)" v-model="item.enable" :disabled="isReadOnly" />
</el-col>
<span style="margin-left: 10px" v-else></span>
<i class="el-icon-top" style="cursor: pointer" @click="moveTop(index)" />
<i class="el-icon-bottom" style="cursor: pointer" @click="moveBottom(index)" />
<el-col class="item">
<el-input
v-if="!suggestions"
:disabled="isReadOnly"
v-model="item.name"
size="small"
maxlength="200"
@change="change"
:placeholder="keyText"
show-word-limit>
<template v-slot:prepend>
<el-select
v-if="type === 'body'"
:disabled="isReadOnly"
class="kv-type"
v-model="item.type"
@change="typeChange(item)">
<el-option value="text" />
<el-option value="file" />
<el-option value="json" />
</el-select>
</template>
</el-input>
<el-autocomplete
:disabled="isReadOnly"
v-if="suggestions"
v-model="item.name"
size="small"
:fetch-suggestions="querySearch"
@change="change"
:placeholder="keyText"
show-word-limit />
</el-col>
<el-col class="item kv-select">
<el-select v-model="item.required" size="small">
<el-option v-for="req in requireds" :key="req.id" :label="req.name" :value="req.id" />
</el-select>
</el-col>
<el-col class="item" v-if="isActive && item.type !== 'file'">
<el-autocomplete
:disabled="isReadOnly"
size="small"
class="input-with-autocomplete"
v-model="item.value"
:fetch-suggestions="funcSearch"
:placeholder="valueText"
value-key="name"
highlight-first-item
@select="change">
<i slot="suffix" class="el-input__icon el-icon-edit pointer" @click="advanced(item)"></i>
</el-autocomplete>
</el-col>
<el-col v-if="isActive && item.type === 'file'" class="item">
<ms-api-body-file-upload :parameter="item" :id="id" :is-read-only="isReadOnly" />
</el-col>
<el-col v-if="type === 'body'" class="item kv-select">
<el-input
:disabled="isReadOnly"
v-model="item.contentType"
size="small"
@change="change"
:placeholder="$t('api_test.request.content_type')"
show-word-limit>
</el-input>
</el-col>
<el-col v-if="withMoreSetting" class="item kv-setting">
<el-tooltip effect="dark" :content="$t('schema.adv_setting')" placement="top">
<i class="el-icon-setting" @click="openApiVariableSetting(item)" />
</el-tooltip>
</el-col>
<el-col class="item kv-delete">
<el-button
size="mini"
class="el-icon-delete-solid"
circle
@click="remove(index)"
:disabled="isDisable(index) || isReadOnly" />
</el-col>
<div style="overflow: auto; width: 100%">
<span class="kv-description" v-if="description">
{{ description }}
</span>
<el-row>
<el-checkbox v-model="isSelectAll" v-if="parameters.length > 1" />
</el-row>
<!-- 数据-->
<div class="item kv-row" v-for="(item, index) in parameters" :key="index" style="width: 99%">
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
<el-col class="kv-checkbox" v-if="isShowEnable">
<el-checkbox v-if="!isDisable(index)" v-model="item.enable" :disabled="isReadOnly" />
</el-col>
<span style="margin-left: 10px" v-else></span>
<i class="el-icon-top" style="cursor: pointer" @click="moveTop(index)" />
<i class="el-icon-bottom" style="cursor: pointer" @click="moveBottom(index)" />
<el-col class="item" style="min-width: 200px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0"> {{ keyText }}</span>
</el-row>
<el-input
v-if="!suggestions"
:disabled="isReadOnly"
v-model="item.name"
size="small"
maxlength="200"
@change="change"
:placeholder="keyText"
show-word-limit>
<template v-slot:prepend>
<el-select
v-if="type === 'body'"
:disabled="isReadOnly"
class="kv-type"
v-model="item.type"
@change="typeChange(item)">
<el-option value="text" />
<el-option value="file" />
<el-option value="json" />
</el-select>
</template>
</el-input>
<el-autocomplete
:disabled="isReadOnly"
v-if="suggestions"
v-model="item.name"
size="small"
:fetch-suggestions="querySearch"
@change="change"
:placeholder="keyText"
show-word-limit />
</el-col>
<el-col class="item kv-select" style="width: 130px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0">
{{ $t('api_test.definition.document.table_coloum.is_required') }}</span
>
</el-row>
<el-select v-model="item.required" size="small" style="width: 120px">
<el-option v-for="req in requireds" :key="req.id" :label="req.name" :value="req.id" />
</el-select>
</el-col>
<el-col class="item" v-if="isActive && item.type !== 'file'" style="min-width: 200px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0"> {{ valueText }}</span>
</el-row>
<el-autocomplete
:disabled="isReadOnly"
size="small"
class="input-with-autocomplete"
v-model="item.value"
:fetch-suggestions="funcSearch"
:placeholder="valueText"
value-key="name"
highlight-first-item
@select="change">
<i slot="suffix" class="el-input__icon el-icon-edit pointer" @click="advanced(item)"></i>
</el-autocomplete>
</el-col>
<el-col v-if="isActive && item.type === 'file'" class="item" style="min-width: 200px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0"> {{ valueText }}</span>
</el-row>
<ms-api-body-file-upload :parameter="item" :id="id" :is-read-only="isReadOnly" />
</el-col>
<el-col v-if="type === 'body'" class="item kv-select" style="min-width: 160px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0"> {{ $t('api_test.request.content_type') }}</span>
</el-row>
<el-input
:disabled="isReadOnly"
v-model="item.contentType"
size="small"
@change="change"
:placeholder="$t('api_test.request.content_type')"
show-word-limit>
</el-input>
</el-col>
<el-col v-if="showColumns('MIX_LENGTH')" class="item kv-select" style="width: 150px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0"> {{ $t('schema.minLength') }}</span>
</el-row>
<el-input-number
:min="0"
v-model="item.min"
:placeholder="$t('schema.minLength')"
size="small"
style="width: 140px" />
</el-col>
<el-col v-if="showColumns('MAX_LENGTH')" class="item kv-select" style="width: 150px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0"> {{ $t('schema.maxLength') }}</span>
</el-row>
<el-input-number
:min="0"
v-model="item.max"
:placeholder="$t('schema.maxLength')"
size="small"
style="width: 140px" />
</el-col>
<el-col v-if="showColumns('ENCODE')" class="item kv-select" style="width: 130px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0"> {{ $t('commons.encode') }}</span>
</el-row>
<el-select v-model="item.urlEncode" size="small" clearable style="width: 100px">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-col>
<el-col class="item" v-if="showColumns('DESCRIPTION')" style="min-width: 300px; padding: 0 5px">
<el-row>
<span class="param-header-span" v-if="index === 0"> {{ $t('commons.description') }}</span>
</el-row>
<el-input
v-model="item.description"
size="small"
maxlength="200"
:placeholder="$t('commons.description')"
show-word-limit>
</el-input>
</el-col>
<el-col v-if="withMoreSetting" class="item kv-setting">
<el-tooltip effect="dark" :content="$t('schema.adv_setting')" placement="top">
<i class="el-icon-setting" @click="openApiVariableSetting(item)" />
</el-tooltip>
</el-col>
<el-col class="item kv-delete">
<el-button
size="mini"
class="el-icon-delete-solid"
circle
@click="remove(index)"
:disabled="isDisable(index) || isReadOnly" />
</el-col>
</el-row>
</div>
</div>
<ms-api-variable-advance
ref="variableAdvance"
:environment="environment"
@ -126,6 +194,7 @@ import MsApiVariableJson from './ApiVariableJson';
import MsApiBodyFileUpload from './body/ApiBodyFileUpload';
import Vue from 'vue';
import ApiVariableSetting from '@/business/definition/components/ApiVariableSetting';
import { getShowFields } from 'metersphere-frontend/src/utils/custom_field';
export default {
name: 'MsApiVariable',
@ -184,6 +253,17 @@ export default {
],
isSelectAll: true,
isActive: true,
paramColumns: [],
options: [
{
value: true,
label: this.$t('commons.yes'),
},
{
value: false,
label: this.$t('commons.no'),
},
],
};
},
watch: {
@ -194,6 +274,13 @@ export default {
this.invertSelect();
}
},
paramColumns: {
handler(val) {
this.reload();
},
deep: true,
},
},
computed: {
keyText() {
@ -204,6 +291,9 @@ export default {
},
},
methods: {
showColumns(columns) {
return this.paramColumns.indexOf(columns) >= 0;
},
moveBottom(index) {
if (this.parameters.length < 2 || index === this.parameters.length - 2) {
return;
@ -365,6 +455,10 @@ export default {
})
);
}
let savedApiParamsShowFields = getShowFields('API_PARAMS_SHOW_FIELD');
if (savedApiParamsShowFields) {
this.paramColumns = savedApiParamsShowFields;
}
},
};
</script>
@ -416,4 +510,9 @@ export default {
width: 40px;
padding: 0px !important;
}
.param-header-span {
margin-bottom: 5px;
font-weight: 600;
}
</style>

View File

@ -26,8 +26,12 @@
</el-radio>
</el-radio-group>
<div v-if="body.type == 'Form Data' || body.type == 'WWW_FORM'">
<el-row v-if="body.type == 'Form Data' || body.type == 'WWW_FORM'">
<el-link class="ms-el-link" @click="batchAdd"> {{ $t('commons.batch_add') }}</el-link>
<el-row v-if="body.type == 'Form Data' || body.type == 'WWW_FORM'" class="ms-el-link">
<el-link style="margin-right: 5px" @click="batchAdd"> {{ $t('commons.batch_add') }}</el-link>
<api-params-config
v-if="apiParamsConfigFields"
@refresh="refreshApiParamsField"
:api-params-config-fields="apiParamsConfigFields" />
</el-row>
<ms-api-variable
:with-more-setting="true"
@ -38,6 +42,7 @@
:scenario-definition="scenarioDefinition"
:id="id"
@editScenarioAdvance="editScenarioAdvance"
v-if="reloadedApiVariable"
type="body" />
</div>
<div v-if="body.type == 'JSON'">
@ -89,6 +94,8 @@ import MsApiBinaryVariable from './ApiBinaryVariable';
import MsApiFromUrlVariable from './ApiFromUrlVariable';
import BatchAddParameter from '../basis/BatchAddParameter';
import Convert from '@/business/commons/json-schema/convert/convert';
import { getApiParamsConfigFields } from 'metersphere-frontend/src/utils/custom_field';
import ApiParamsConfig from '@/business/definition/components/request/components/ApiParamsConfig';
export default {
name: 'MsApiBody',
@ -101,6 +108,7 @@ export default {
MsApiFromUrlVariable,
MsJsonCodeEdit,
BatchAddParameter,
ApiParamsConfig,
},
props: {
body: {
@ -131,6 +139,8 @@ export default {
},
data() {
return {
reloadedApiVariable: true,
apiParamsConfigFields: getApiParamsConfigFields(this),
type: BODY_TYPE,
modes: ['text', 'json', 'xml', 'html'],
jsonSchema: 'JSON',
@ -146,6 +156,13 @@ export default {
},
},
methods: {
refreshApiParamsField() {
this.reloadedApiVariable = false;
this.$nextTick(() => {
this.reloadedApiVariable = true;
});
},
isObj(x) {
let type = typeof x;
return x !== null && (type === 'object' || type === 'function');

View File

@ -36,8 +36,17 @@
</div>
</div>
<div v-else>
<el-row v-if="tableType === 'rest' || tableType === 'query'">
<div style="float: right">
<api-params-config
v-if="apiParamsConfigFields"
@refresh="refreshApiParamsField"
:api-params-config-fields="apiParamsConfigFields" />
</div>
</el-row>
<el-table
border
v-if="reloadedApiVariable"
:show-header="true"
row-key="id"
:row-class-name="getRowClassName"
@ -78,37 +87,25 @@
<script>
import { getCurrentUser } from 'metersphere-frontend/src/utils/token';
import { getUUID } from 'metersphere-frontend/src/utils';
import tableAdvancedSetting from '@/business/definition/components/document/components/plugin/TableAdvancedSetting';
import TableAdvancedSetting from '@/business/definition/components/document/components/plugin/TableAdvancedSetting';
import ApiParamsConfig from '@/business/definition/components/request/components/ApiParamsConfig';
import { getApiParamsConfigFields, getShowFields } from 'metersphere-frontend/src/utils/custom_field';
export default {
name: 'ApiInfoCollapse',
components: { tableAdvancedSetting },
components: { TableAdvancedSetting, ApiParamsConfig },
data() {
return {
active: true,
expandAllRow: false,
language: 'zh_CN',
reloadedApiVariable: true,
tableData: [],
storageKey: 'API_PARAMS_SHOW_FIELD',
apiParamsConfigFields: getApiParamsConfigFields(this),
tableExpandButtonId: 'docTableExpandBtn' + getUUID(),
expandTitle: this.$t('commons.expand_all'),
tableColumnArr: [
{ id: 1, prop: 'name', label: this.$t('api_definition.document.name') },
{
id: 2,
prop: 'isRequired',
label: this.$t('api_definition.document.is_required'),
},
{
id: 3,
prop: 'value',
label: this.$t('api_definition.document.value'),
},
{
id: 4,
prop: 'description',
label: this.$t('api_definition.document.desc'),
},
],
tableColumnArr: [],
};
},
props: {
@ -116,6 +113,7 @@ export default {
tableColumnType: String,
remarks: String,
isRequest: Boolean,
tableType: String,
isResponse: Boolean,
tableCanExpand: {
type: Boolean,
@ -138,6 +136,7 @@ export default {
this.language = user.language;
}
this.tableData = this.getJsonArr(this.stringData);
this.formatTableData();
},
created: function () {
//language zh_CN/zh_TW/en_US
@ -145,7 +144,9 @@ export default {
if (user) {
this.language = user.language;
}
this.initTableColumnArr();
this.tableData = this.getJsonArr(this.stringData);
this.formatTableData();
},
mounted() {
//language zh_CN/zh_TW/en_US
@ -154,6 +155,7 @@ export default {
this.language = user.language;
}
this.tableData = this.getJsonArr(this.stringData);
this.formatTableData();
},
computed: {
showSlotComponent() {
@ -163,6 +165,7 @@ export default {
watch: {
stringData() {
this.tableData = this.getJsonArr(this.stringData);
this.formatTableData();
},
expandAllRow() {
if (this.$refs.expandTable) {
@ -184,6 +187,71 @@ export default {
},
},
methods: {
formatTableData() {
if (this.tableData) {
this.tableData.forEach((item) => {
if (item.urlEncode !== null && item.urlEncode !== undefined) {
if (item.urlEncode === true) {
item.urlEncode = this.$t('commons.yes');
} else {
item.urlEncode = this.$t('commons.no');
}
}
});
}
},
refreshApiParamsField() {
this.initTableColumnArr();
this.reloadedApiVariable = false;
this.$nextTick(() => {
this.reloadedApiVariable = true;
});
},
initTableColumnArr() {
this.tableColumnArr = [
{ id: 1, prop: 'name', label: this.$t('api_definition.document.name') },
{
id: 2,
prop: 'isRequired',
label: this.$t('api_definition.document.is_required'),
},
{
id: 3,
prop: 'value',
label: this.$t('api_definition.document.value'),
},
];
if (this.tableType === 'rest' || this.tableType === 'query') {
let apiParamConfigArr = getShowFields(this.storageKey);
if (apiParamConfigArr) {
apiParamConfigArr.forEach((item) => {
let tableColumn = {};
if (item === 'MIX_LENGTH') {
tableColumn.id = 5;
tableColumn.prop = 'min';
tableColumn.label = this.$t('schema.minLength');
} else if (item === 'MAX_LENGTH') {
tableColumn.id = 6;
tableColumn.prop = 'max';
tableColumn.label = this.$t('schema.maxLength');
} else if (item === 'ENCODE') {
tableColumn.id = 7;
tableColumn.prop = 'urlEncode';
tableColumn.label = this.$t('commons.encode');
} else if (item === 'DESCRIPTION') {
tableColumn.id = 8;
tableColumn.prop = 'description';
tableColumn.label = this.$t('commons.description');
} else {
tableColumn = null;
}
if (tableColumn) {
this.tableColumnArr.push(tableColumn);
}
});
}
}
},
getRowClassName({ row, rowIndex }) {
let classname = 'autofix-table-row ';
//

View File

@ -50,12 +50,14 @@
<api-info-collapse
v-if="isArrayHasData(apiInfo.urlParams)"
table-column-type="simple"
table-type="query"
:title="'QUERY' + $t('api_test.definition.document.request_param')"
:string-data="apiInfo.urlParams" />
<!--REST参数-->
<api-info-collapse
v-if="isArrayHasData(apiInfo.restParams)"
table-column-type="simple"
table-type="rest"
:title="'REST' + $t('api_test.definition.document.request_param')"
:string-data="apiInfo.restParams" />
<!--api请求体 以及表格-->

View File

@ -2,54 +2,44 @@
<div>
<el-row class="apiInfoRow">
<div>
<el-table
border
v-if="formParamTypes.includes(apiInfo.requestBodyParamType)"
:show-header="true"
row-key="id"
:row-class-name="getRowClassName"
:data="tableData"
:class="getTableClass()"
ref="expandTable">
<el-table-column
prop="name"
:label="$t('api_definition.document.name')"
min-width="120px"
show-overflow-tooltip />
<el-table-column
prop="contentType"
:label="$t('api_definition.document.type')"
min-width="120px"
show-overflow-tooltip />
<el-table-column
prop="description"
:label="$t('api_definition.document.desc')"
min-width="280px"
show-overflow-tooltip />
<el-table-column
prop="required"
:label="$t('api_definition.document.is_required')"
:formatter="formatBoolean"
min-width="80px"
show-overflow-tooltip />
<el-table-column
prop="value"
:label="$t('api_definition.document.default_value')"
min-width="120px"
show-overflow-tooltip />
<el-table-column type="expand" :label="getCollapseOption()" width="80px">
<template slot="header">
<el-button type="text" size="mini" @click="expandAllRows">
<span :id="tableExpandButtonId">
{{ expandTitle }}
</span>
</el-button>
</template>
<template v-slot:default="scope">
<table-advanced-setting :table-data="scope.row"></table-advanced-setting>
</template>
</el-table-column>
</el-table>
<div v-if="formParamTypes.includes(apiInfo.requestBodyParamType)">
<el-row>
<div style="float: right">
<api-params-config
v-if="apiParamsConfigFields"
@refresh="refreshApiParamsField"
:api-params-config-fields="apiParamsConfigFields" />
</div>
</el-row>
<el-table
border
:show-header="true"
row-key="id"
:row-class-name="getRowClassName"
:data="tableData"
:class="getTableClass()"
ref="expandTable">
<el-table-column
v-for="item in tableColumnArr"
:key="item.id"
:prop="item.prop"
:label="item.label"
show-overflow-tooltip />
<el-table-column type="expand" :label="getCollapseOption()" width="80px">
<template slot="header">
<el-button type="text" size="mini" @click="expandAllRows">
<span :id="tableExpandButtonId">
{{ expandTitle }}
</span>
</el-button>
</template>
<template v-slot:default="scope">
<table-advanced-setting :table-data="scope.row"></table-advanced-setting>
</template>
</el-table-column>
</el-table>
</div>
<div
v-else-if="apiInfo.requestBodyParamType === 'JSON-SCHEMA' || apiInfo.requestBodyParamType === 'JSON'"
style="margin-left: 10px">
@ -87,10 +77,12 @@ import JsonSchemaShow from '@/business/definition/components/document/components
import tableAdvancedSetting from '@/business/definition/components/document/components/plugin/TableAdvancedSetting';
import { getCurrentUser } from 'metersphere-frontend/src/utils/token';
import { getUUID } from 'metersphere-frontend/src/utils';
import { getApiParamsConfigFields, getShowFields } from 'metersphere-frontend/src/utils/custom_field';
import ApiParamsConfig from '@/business/definition/components/request/components/ApiParamsConfig';
export default {
name: 'ApiRequestInfo',
components: { JsonSchemaShow, tableAdvancedSetting },
components: { JsonSchemaShow, tableAdvancedSetting, ApiParamsConfig },
data() {
return {
tableData: [],
@ -98,8 +90,11 @@ export default {
tableExpandButtonId: 'docTableExpandBtn' + getUUID(),
active: true,
expandAllRow: false,
apiParamStorageKey: 'API_PARAMS_SHOW_FIELD',
expandTitle: this.$t('commons.expand_all'),
apiParamsConfigFields: getApiParamsConfigFields(this),
formParamTypes: ['form-data', 'x-www-from-urlencoded', 'BINARY'],
tableColumnArr: [],
};
},
props: {
@ -108,38 +103,45 @@ export default {
activated() {
if (this.apiInfo && this.apiInfo.requestBodyFormData) {
this.tableData = this.getJsonArr(this.apiInfo.requestBodyFormData);
this.formatTableData();
}
//language zh_CN/zh_TW/en_US
let user = getCurrentUser();
if (user) {
this.language = user.language;
}
this.initTableColumn();
},
created: function () {
if (this.apiInfo && this.apiInfo.requestBodyFormData) {
this.tableData = this.getJsonArr(this.apiInfo.requestBodyFormData);
this.formatTableData();
}
//language zh_CN/zh_TW/en_US
let user = getCurrentUser();
if (user) {
this.language = user.language;
}
this.initTableColumn();
},
mounted() {
if (this.apiInfo && this.apiInfo.requestBodyFormData) {
this.tableData = this.getJsonArr(this.apiInfo.requestBodyFormData);
this.formatTableData();
}
//language zh_CN/zh_TW/en_US
let user = getCurrentUser();
if (user) {
this.language = user.language;
}
this.initTableColumn();
},
computed: {},
watch: {
'apiInfo.requestBodyFormData': {
handler(v) {
this.tableData = this.getJsonArr(this.apiInfo.requestBodyFormData);
this.formatTableData();
},
deep: true,
},
@ -163,6 +165,79 @@ export default {
},
},
methods: {
formatTableData() {
if (this.tableData) {
this.tableData.forEach((item) => {
if (item.urlEncode !== null && item.urlEncode !== undefined) {
if (item.urlEncode === true) {
item.urlEncode = this.$t('commons.yes');
} else {
item.urlEncode = this.$t('commons.no');
}
}
if (item.enable !== null && item.enable !== undefined) {
if (item.enable === true) {
item.enable = this.$t('commons.yes');
} else {
item.enable = this.$t('commons.no');
}
}
});
}
},
refreshApiParamsField() {
this.initTableColumn();
this.reloadedApiVariable = false;
this.$nextTick(() => {
this.reloadedApiVariable = true;
});
},
initTableColumn() {
this.tableColumnArr = [
{ id: 1, prop: 'name', label: this.$t('api_definition.document.name') },
{ id: 2, prop: 'contentType', label: this.$t('api_definition.document.type') },
{
id: 3,
prop: 'enable',
label: this.$t('api_definition.document.is_required'),
},
{
id: 4,
prop: 'value',
label: this.$t('api_definition.document.value'),
},
];
if (this.formParamTypes.includes(this.apiInfo.requestBodyParamType)) {
let apiParamConfigArr = getShowFields(this.apiParamStorageKey);
if (apiParamConfigArr) {
apiParamConfigArr.forEach((item) => {
let tableColumn = {};
if (item === 'MIX_LENGTH') {
tableColumn.id = 5;
tableColumn.prop = 'min';
tableColumn.label = this.$t('schema.minLength');
} else if (item === 'MAX_LENGTH') {
tableColumn.id = 6;
tableColumn.prop = 'max';
tableColumn.label = this.$t('schema.maxLength');
} else if (item === 'ENCODE') {
tableColumn.id = 7;
tableColumn.prop = 'urlEncode';
tableColumn.label = this.$t('commons.encode');
} else if (item === 'DESCRIPTION') {
tableColumn.id = 8;
tableColumn.prop = 'description';
tableColumn.label = this.$t('commons.description');
} else {
tableColumn = null;
}
if (tableColumn) {
this.tableColumnArr.push(tableColumn);
}
});
}
}
},
getRowClassName({ row, rowIndex }) {
let classname = 'autofix-table-row ';
//

View File

@ -1,7 +1,7 @@
<template>
<div class="json-schema-editor">
<el-row class="row" :gutter="20">
<el-col :span="8" class="ms-col-name">
<div class="json-schema-editor" style="padding: 0 10px">
<el-row class="row" :gutter="10">
<el-col :style="{ minWidth: `${200 - 10 * deep}px` }" class="ms-col-name">
<div :style="{ marginLeft: `${10 * deep}px` }" class="ms-col-name-c" />
<span
v-if="pickValue.type === 'object' || pickValue.type === 'array'"
@ -16,17 +16,18 @@
@blur="onInputName"
size="small" />
</el-col>
<el-col :span="4">
<el-col style="width: 120px; padding: 0 5px">
<el-select
v-model="pickValue.type"
:disabled="disabled || disabledType"
class="ms-col-type"
@change="onChangeType"
style="width: 110px"
size="small">
<el-option :key="t" :value="t" :label="t" v-for="t in types" />
</el-select>
</el-col>
<el-col :span="5">
<el-col style="min-width: 200px; padding: 0 5px">
<ms-mock
:disabled="
disabled ||
@ -38,9 +39,74 @@
:schema="pickValue"
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
style="width: 100%"
@editScenarioAdvance="editScenarioAdvance" />
</el-col>
<el-col :span="5">
<el-col v-if="showColumns('MIX_LENGTH')" class="item kv-select" style="width: 150px; padding: 0 5px">
<el-input-number
:min="0"
v-model="pickValue.minLength"
:placeholder="$t('schema.minLength')"
size="small"
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
style="width: 140px" />
</el-col>
<el-col v-if="showColumns('MAX_LENGTH')" class="item kv-select" style="width: 150px; padding: 0 5px">
<el-input-number
:min="0"
v-model="pickValue.maxLength"
:placeholder="$t('schema.maxLength')"
size="small"
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
style="width: 140px" />
</el-col>
<el-col v-if="showColumns('DEFAULT')" class="item kv-select" style="min-width: 200px; padding: 0 5px">
<el-input
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
v-model="pickValue.default"
class="ms-col-title"
:placeholder="$t('schema.default')"
style="width: 100%"
size="small" />
</el-col>
<el-col v-if="showColumns('PATTERN')" style="min-width: 200px; padding: 0 5px">
<el-input
:disabled="
disabled ||
pickValue.type === 'object' ||
pickKey === 'root' ||
pickValue.type === 'array' ||
pickValue.type === 'null'
"
v-model="pickValue.pattern"
class="ms-col-title"
:placeholder="$t('schema.pattern')"
size="small" />
</el-col>
<el-col v-if="showColumns('FORMAT')" style="min-width: 120px; padding: 0 5px">
<el-input :disabled="true" size="small" :placeholder="$t('schema.format')"></el-input>
</el-col>
<el-col v-if="showColumns('ENUM')" style="min-width: 300px; padding: 0 5px">
<el-input
:disabled="disabled"
v-model="pickValue.description"
@ -48,19 +114,7 @@
:placeholder="$t('schema.description')"
size="small" />
</el-col>
<el-col :span="2">
<div v-if="hasAdvancedSetting">
<el-link @click="changeCollapseStatus">{{ getCollapseOption() }}</el-link>
</div>
</el-col>
</el-row>
<div>
<el-collapse-transition>
<div v-show="collapseStatus && hasAdvancedSetting" :style="{ marginLeft: `${10 * deep + 10}px` }">
<json-advanced-setting :json-data="pickValue" />
</div>
</el-collapse-transition>
</div>
<template v-if="!hidden && pickValue.properties && !isArray && reloadItemOver">
<json-schema-panel
@ -71,6 +125,7 @@
:deep="deep + 1"
:root="false"
class="children"
:param-columns="paramColumns"
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
:disabled="disabled"
@ -89,6 +144,7 @@
:deep="deep + 1"
:root="false"
class="children"
:param-columns="paramColumns"
:scenario-definition="scenarioDefinition"
:show-mock-vars="showMockVars"
:expand-all-params="expandAllParams"
@ -124,6 +180,7 @@ export default {
type: Boolean,
default: false,
},
paramColumns: Array,
disabledType: {
//
type: Boolean,
@ -150,12 +207,10 @@ export default {
default: null,
},
custom: {
//enable custom properties
type: Boolean,
default: false,
},
lang: {
// i18n language
type: String,
default: 'zh_CN',
},
@ -248,6 +303,12 @@ export default {
},
},
methods: {
showColumns(columns) {
if (!this.paramColumns) {
return false;
}
return this.paramColumns.indexOf(columns) >= 0;
},
isNotEmptyValue(value) {
return value && value !== '';
},

View File

@ -1,36 +1,49 @@
<template>
<div id="app" v-loading="loading">
<el-row>
<el-col>
<el-button style="float: right" type="text" size="mini" @click="expandAll">
<el-col></el-col>
<div style="float: right">
<el-button style="margin-right: 5px" type="text" size="mini" @click="expandAll">
{{ expandTitle }}
</el-button>
</el-col>
<api-params-config
v-if="apiJsonSchemaConfigFields"
:storage-key="storageKey"
@refresh="refreshApiParamsField"
:api-params-config-fields="apiJsonSchemaConfigFields" />
</div>
</el-row>
<div :style="jsonSchemaDisable ? '' : 'min-height: 200px'">
<json-schema-panel
class="schema"
:disabled="jsonSchemaDisable"
:value="schema"
:show-mock-vars="showMockVars"
:scenario-definition="scenarioDefinition"
:expand-all-params="expandAllParams"
@editScenarioAdvance="editScenarioAdvance"
lang="zh_CN"
custom />
<div style="overflow: auto">
<json-schema-panel
class="schema"
v-if="reloadedApiVariable"
:disabled="jsonSchemaDisable"
:value="schema"
:show-mock-vars="showMockVars"
:scenario-definition="scenarioDefinition"
:param-columns="apiJsonSchemaShowColumns"
:expand-all-params="expandAllParams"
@editScenarioAdvance="editScenarioAdvance"
lang="zh_CN"
custom />
</div>
</div>
</div>
</template>
<script>
import JsonSchemaPanel from '@/business/definition/components/document/components/JsonSchema/JsonSchemaPanel';
import { getApiJsonSchemaConfigFields, getShowFields } from 'metersphere-frontend/src/utils/custom_field';
import ApiParamsConfig from '@/business/definition/components/request/components/ApiParamsConfig';
const Convert = require('@/business/commons/json-schema/convert/convert.js');
const MsConvert = new Convert();
export default {
name: 'JsonSchemaShow',
components: { JsonSchemaPanel },
components: { JsonSchemaPanel, ApiParamsConfig },
props: {
body: {},
showPreview: {
@ -57,6 +70,7 @@ export default {
this.schema = { root: this.body.jsonSchema };
}
this.body.jsonSchema = this.schema.root;
this.apiJsonSchemaShowColumns = getShowFields(this.storageKey);
},
watch: {
schema: {
@ -86,6 +100,10 @@ export default {
properties: {},
},
},
reloadedApiVariable: true,
storageKey: 'API_JSON_SCHEMA_SHOW_FIELD',
apiJsonSchemaConfigFields: getApiJsonSchemaConfigFields(this),
apiJsonSchemaShowColumns: [],
loading: false,
expandAllParams: false,
};
@ -96,6 +114,9 @@ export default {
},
},
methods: {
refreshApiParamsField() {
this.apiJsonSchemaShowColumns = getShowFields(this.storageKey);
},
expandAll() {
this.expandAllParams = !this.expandAllParams;
},

View File

@ -0,0 +1,63 @@
<template>
<el-popover placement="top" width="100" v-model="isActive">
<div v-for="item in apiParamsConfigFields" :key="item.value">
<el-checkbox-group v-model="checkList">
<el-checkbox :label="item.value">{{ item.text }}</el-checkbox>
</el-checkbox-group>
</div>
<div style="text-align: right; margin: 0">
<el-button size="mini" type="text" @click="cancel">{{ $t('commons.cancel') }}</el-button>
<el-button type="primary" size="mini" @click="confirm">{{ $t('commons.confirm') }}</el-button>
</div>
<i slot="reference" class="el-icon-setting" />
</el-popover>
</template>
<script>
import { getShowFields } from 'metersphere-frontend/src/utils/custom_field';
export default {
name: 'ApiParamsConfig',
data() {
return {
isActive: false,
checkList: [],
};
},
props: {
apiParamsConfigFields: Array,
showColumns: Array,
storageKey: {
type: String,
default() {
return 'API_PARAMS_SHOW_FIELD';
},
},
},
watch: {
isActive() {
if (this.isActive) {
this.checkList = this.getCheckList(this.storageKey);
}
},
},
methods: {
cancel() {
this.isActive = false;
},
confirm() {
let apiParamsShowFields = JSON.stringify(this.checkList);
localStorage.setItem(this.storageKey, apiParamsShowFields);
this.$nextTick(() => {
this.$emit('refresh');
this.isActive = false;
});
},
getCheckList(fieldKey) {
return getShowFields(fieldKey);
},
},
};
</script>
<style scoped></style>

View File

@ -52,10 +52,14 @@
</div>
</span>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: var(--primary_color)">
<el-row class="ms-el-link">
<el-link @click="batchAdd" style="margin-right: 5px; color: var(--primary_color)">
{{ $t('commons.batch_add') }}
</el-link>
<api-params-config
v-if="apiParamsConfigFields"
@refresh="refreshApiParamsField"
:api-params-config-fields="apiParamsConfigFields" />
</el-row>
<ms-api-variable
@editScenarioAdvance="editScenarioAdvance"
@ -84,10 +88,14 @@
</div>
</span>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: var(--primary_color)">
<el-row class="ms-el-link">
<el-link @click="batchAdd" style="margin-right: 5px; color: var(--primary_color)">
{{ $t('commons.batch_add') }}
</el-link>
<api-params-config
v-if="apiParamsConfigFields"
@refresh="refreshApiParamsField"
:api-params-config-fields="apiParamsConfigFields" />
</el-row>
<ms-api-variable
@editScenarioAdvance="editScenarioAdvance"
@ -197,6 +205,7 @@ import MsApiBody from '../../body/ApiBody';
import MsApiAuthConfig from '../../auth/ApiAuthConfig';
import ApiRequestMethodSelect from '../../collapse/ApiRequestMethodSelect';
import { REQUEST_HEADERS } from 'metersphere-frontend/src/utils/constants';
import { getApiParamsConfigFields } from 'metersphere-frontend/src/utils/custom_field';
import MsApiVariable from '../../ApiVariable';
import MsApiAssertions from '../../assertion/ApiAssertions';
import MsApiExtract from '../../extract/ApiExtract';
@ -208,6 +217,7 @@ import MsApiAdvancedConfig from './ApiAdvancedConfig';
import MsJsr233Processor from '@/business/automation/scenario/component/Jsr233Processor';
import Convert from '@/business/commons/json-schema/convert/convert';
import { hisDataProcessing, stepCompute } from '@/business/definition/api-definition';
import ApiParamsConfig from '@/business/definition/components/request/components/ApiParamsConfig';
export default {
name: 'MsApiHttpRequestForm',
@ -222,6 +232,7 @@ export default {
MsApiBody,
MsApiKeyValue,
MsApiAssertions,
ApiParamsConfig,
MsJmxStep: () => import('@/business/definition/components/step/JmxStep'),
},
props: {
@ -273,6 +284,8 @@ export default {
};
return {
activeName: this.request.method === 'POST' ? 'body' : 'parameters',
queryColumnConfig: false,
apiParamsConfigFields: getApiParamsConfigFields(this),
rules: {
name: [
{
@ -336,6 +349,13 @@ export default {
});
});
},
refreshApiParamsField() {
let oldActiveName = this.activeName;
this.activeName = 'refreshing';
this.$nextTick(() => {
this.activeName = oldActiveName;
});
},
changeActiveName() {
if (this.request.headers && this.request.headers.length > 1) {
this.activeName = 'headers';

View File

@ -8,11 +8,14 @@
v-loading="tableIsLoading"
:data="data"
:default-sort="defaultSort"
:class="{'ms-select-all-fixed': (showSelectAll && !hidePopover), 'row-click': rowClickStyle}"
:class="{
'ms-select-all-fixed': showSelectAll && !hidePopover,
'row-click': rowClickStyle,
}"
:height="screenHeight"
:row-key="rowKey"
:row-class-name="tableRowClassName"
:row-style='rowStyle'
:row-style="rowStyle"
:cell-class-name="addPaddingColClass"
:highlight-current-row="highlightCurrentRow"
@sort-change="sort"
@ -22,35 +25,37 @@
@header-dragend="headerDragend"
@cell-mouse-enter="showPopover"
@row-click="handleRowClick"
ref="table">
ref="table"
>
<el-table-column v-if="enableSelection" width="50" type="selection" />
<ms-table-header-select-popover
v-if="enableSelection && showSelectAll && !hidePopover"
:page-size="pageSize > total ? total : pageSize"
:table-data-count-in-page="data.length"
:total="total"
:select-type="condition.selectAll"
@selectPageAll="isSelectDataAll(false)"
@selectAll="isSelectDataAll(true)"
ref="selectPopover"
/>
<el-table-column
v-if="enableSelection"
width="50"
type="selection"/>
<ms-table-header-select-popover v-if="enableSelection && showSelectAll && !hidePopover"
:page-size="pageSize > total ? total : pageSize"
:table-data-count-in-page="data.length"
:total="total"
:select-type="condition.selectAll"
@selectPageAll="isSelectDataAll(false)"
@selectAll="isSelectDataAll(true)"
ref="selectPopover"/>
<el-table-column v-if="enableSelection && batchOperators && batchOperators.length > 0"
width="15"
fixed="left"
column-key="batchBtnCol"
align="center"
:resizable="false">
v-if="enableSelection && batchOperators && batchOperators.length > 0"
width="15"
fixed="left"
column-key="batchBtnCol"
align="center"
:resizable="false"
>
<template v-slot:default="scope">
<!-- 选中记录后浮现的按钮提供对记录的批量操作 -->
<show-more-btn :has-showed="hasBatchTipShow"
:is-show="scope.row.showMore"
:buttons="batchOperators"
:size="selectDataCounts"/>
<show-more-btn
:has-showed="hasBatchTipShow"
:is-show="scope.row.showMore"
:buttons="batchOperators"
:size="selectDataCounts"
/>
</template>
</el-table-column>
@ -64,11 +69,12 @@
<el-table-column
v-if="enableOrderDrag"
width="20"
column-key="tableRowDropCol">
column-key="tableRowDropCol"
>
<template v-slot:default="scope">
<div class="table-row-drop-bar">
<i class="el-icon-more ms-icon-more"/>
<i class="el-icon-more ms-icon-more"/>
<i class="el-icon-more ms-icon-more" />
<i class="el-icon-more ms-icon-more" />
</div>
</template>
</el-table-column>
@ -79,28 +85,24 @@
v-if="operators && operators.length > 0"
:fixed="operatorFixed"
:min-width="operatorWidth"
:label="$t('commons.operating')">
:label="$t('commons.operating')"
>
<template slot="header">
<header-label-operate
v-if="fieldKey"
:disable-header-config="disableHeaderConfig"
@exec="openCustomHeader"/>
@exec="openCustomHeader"
/>
</template>
<template
v-slot:default="scope">
<template v-slot:default="scope">
<div>
<slot
name="opt-before"
:row="scope.row">
</slot>
<slot name="opt-before" :row="scope.row"></slot>
<ms-table-operators
:buttons="operators"
:row="scope.row"
:index="scope.$index"/>
<slot
name="opt-behind"
:row="scope.row">
</slot>
:index="scope.$index"
/>
<slot name="opt-behind" :row="scope.row"></slot>
</div>
</template>
</el-table-column>
@ -111,8 +113,8 @@
:type="fieldKey"
:custom-fields="customFields"
@reload="resetHeader"
ref="customTableHeader"/>
ref="customTableHeader"
/>
</div>
</template>
@ -122,15 +124,15 @@ import {
_handleSelect,
_handleSelectAll,
_sort,
getSelectDataCounts,
setUnSelectIds,
toggleAllSelection,
checkTableRowIsSelect,
clearShareDragParam,
getCustomTableHeader,
getSelectDataCounts,
handleRowDrop,
saveCustomTableWidth,
saveLastTableSortField,
handleRowDrop,
clearShareDragParam,
setUnSelectIds,
toggleAllSelection,
} from "../../utils/tableUtils";
import MsTableHeaderSelectPopover from "./MsTableHeaderSelectPopover";
import MsTablePagination from "../pagination/TablePagination";
@ -140,8 +142,7 @@ import MsTableOperators from "../MsTableOperators";
import HeaderLabelOperate from "../head/HeaderLabelOperate";
import HeaderCustom from "../head/HeaderCustom";
import MsCustomTableHeader from "./MsCustomTableHeader";
import {lineToHump} from "../../utils";
import {getUUID} from "../../utils";
import { getUUID, lineToHump } from "../../utils";
/**
* 参考 ApiList
@ -160,7 +161,12 @@ export default {
components: {
MsCustomTableHeader,
HeaderLabelOperate,
MsTableOperators, MsTableColumn, ShowMoreBtn, MsTablePagination, MsTableHeaderSelectPopover, HeaderCustom
MsTableOperators,
MsTableColumn,
ShowMoreBtn,
MsTablePagination,
MsTableHeaderSelectPopover,
HeaderCustom,
},
data() {
return {
@ -182,91 +188,91 @@ export default {
type: Boolean,
default() {
return false;
}
},
},
selectNodeIds: {
type: Array,
default() {
return [];
}
},
},
data: {
type: Array,
default() {
return [];
}
},
},
condition: {
type: Object,
default() {
return {};
}
},
},
pageSize: {
type: Number,
default() {
return 10;
}
},
},
total: {
type: Number,
default() {
return 10;
}
},
},
//
operators: {
type: Array,
default() {
return [];
}
},
},
//
batchOperators: {
type: Array,
default() {
return [];
}
},
},
//
operatorWidth: {
type: String,
default() {
return "150px";
}
},
},
//
operatorFixed: {
type: [String, Boolean],
default() {
return "right";
}
},
},
//
enableSelection: {
type: Boolean,
default() {
return true;
}
},
}, //
showSelectAll: {
type: Boolean,
default() {
return true;
}
},
},
//
rowClickStyle: {
type: Boolean,
default() {
return false;
}
},
},
tableIsLoading: {
type: [Boolean, Promise],
default() {
return false;
}
},
},
disableHeaderConfig: Boolean,
fields: Array,
@ -279,10 +285,9 @@ export default {
rowKey: [String, Function],
// idid
rowOrderGroupId: String,
rowOrderFunc: Function
},
created() {
rowOrderFunc: Function,
},
created() {},
mounted() {
this.setDefaultOrders();
},
@ -310,15 +315,16 @@ export default {
},
selectDataCounts(value) {
this.$emit("selectCountChange", value);
}
},
},
methods: {
// , ,
// batch-popper , ,
removeBatchPopper() {
let elements = window.document.getElementsByClassName('batch-popper');
let tableHeader = window.document.getElementsByClassName('table-column-mark');
let columns = window.document.getElementsByClassName('table-more-icon');
let elements = window.document.getElementsByClassName("batch-popper");
let tableHeader =
window.document.getElementsByClassName("table-column-mark");
let columns = window.document.getElementsByClassName("table-more-icon");
let tableTop = tableHeader[0].getBoundingClientRect().top;
let index = 0;
for (let i = 0; i < columns.length; i++) {
@ -331,7 +337,7 @@ export default {
if (elements) {
for (let i = 0; i < elements.length; i++) {
if (i == index) {
elements[i].classList.remove('batch-popper');
elements[i].classList.remove("batch-popper");
setTimeout(() => {
this.hasBatchTipShow = true;
}, 1500);
@ -342,15 +348,20 @@ export default {
//
listenRowDrop() {
if (this.rowOrderGroupId) {
handleRowDrop(this.data, (param) => {
param.groupId = this.rowOrderGroupId;
if (this.rowOrderFunc) {
this.rowOrderFunc(param);
}
}, this.msTableKey);
handleRowDrop(
this.data,
(param) => {
param.groupId = this.rowOrderGroupId;
if (this.rowOrderFunc) {
this.rowOrderFunc(param);
}
},
this.msTableKey
);
}
},
isScrollShow(column, tableTop) { //
isScrollShow(column, tableTop) {
//
let columnTop = column.getBoundingClientRect().top;
return columnTop - tableTop > 30;
},
@ -358,25 +369,35 @@ export default {
setDefaultOrders() {
let orders = this.condition.orders;
if (orders) {
orders.forEach(item => {
orders.forEach((item) => {
this.defaultSort = {
prop: lineToHump(item.name),
order: 'descending'
order: "descending",
};
if (item.type === 'asc') {
this.defaultSort.order = 'ascending';
if (item.type === "asc") {
this.defaultSort.order = "ascending";
}
return;
});
}
},
handleSelectAll(selection) {
_handleSelectAll(this, selection, this.data, this.selectRows, this.condition);
_handleSelectAll(
this,
selection,
this.data,
this.selectRows,
this.condition
);
setUnSelectIds(this.data, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.selectIds = Array.from(this.selectRows).map(o => o.id);
this.selectDataCounts = getSelectDataCounts(
this.condition,
this.total,
this.selectRows
);
this.selectIds = Array.from(this.selectRows).map((o) => o.id);
//
this.$emit('callBackSelectAll', selection);
this.$emit("callBackSelectAll", selection);
this.$nextTick(function () {
setTimeout(this.removeBatchPopper, 1);
});
@ -384,10 +405,14 @@ export default {
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
setUnSelectIds(this.data, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.selectIds = Array.from(this.selectRows).map(o => o.id);
this.selectDataCounts = getSelectDataCounts(
this.condition,
this.total,
this.selectRows
);
this.selectIds = Array.from(this.selectRows).map((o) => o.id);
//
this.$emit('callBackSelect', selection);
this.$emit("callBackSelect", selection);
this.$nextTick(function () {
setTimeout(this.removeBatchPopper, 1);
});
@ -401,8 +426,12 @@ export default {
//ID()
this.condition.unSelectIds = [];
//
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
this.selectIds = Array.from(this.selectRows).map(o => o.id);
this.selectDataCounts = getSelectDataCounts(
this.condition,
this.total,
this.selectRows
);
this.selectIds = Array.from(this.selectRows).map((o) => o.id);
},
headerDragend(newWidth, oldWidth, column, event) {
if (column) {
@ -417,11 +446,6 @@ export default {
//
saveCustomTableWidth(this.fieldKey, column.columnKey, newWidth);
},
showPopover(row, column, cell) {
if (column.property === 'name') {
this.currentCaseId = row.id;
}
},
doLayout() {
if (this.$refs.table) {
//
@ -430,9 +454,14 @@ export default {
}
}
},
showPopover(row, column, cell) {
if (column.property === "name") {
this.currentCaseId = row.id;
}
},
filter(filters) {
_filter(filters, this.condition);
this.$emit('filter');
this.$emit("filter");
this.handleRefresh();
},
sort(column) {
@ -442,9 +471,12 @@ export default {
}
_sort(column, this.condition);
if (this.rememberOrder) {
saveLastTableSortField(this.fieldKey, JSON.stringify(this.condition.orders));
saveLastTableSortField(
this.fieldKey,
JSON.stringify(this.condition.orders)
);
}
this.$emit('order', column);
this.$emit("order", column);
this.handleRefresh();
},
handleBatchEdit() {
@ -452,17 +484,21 @@ export default {
this.$refs.batchEdit.open();
},
handleBatchMove() {
this.$refs.testBatchMove.open(this.treeNodes, Array.from(this.selectRows).map(row => row.id), this.moduleOptions);
this.$refs.testBatchMove.open(
this.treeNodes,
Array.from(this.selectRows).map((row) => row.id),
this.moduleOptions
);
},
handleRowClick(row, column) {
this.$emit("handleRowClick", row, column);
},
handleRefresh() {
this.clear();
this.$emit('refresh');
this.$emit("refresh");
},
handlePageChange() {
this.$emit('pageChange');
this.$emit("pageChange");
},
cancelCurrentRow() {
this.$refs.table.setCurrentRow(-1);
@ -474,7 +510,13 @@ export default {
this.clearSelectRows();
},
checkTableRowIsSelect() {
checkTableRowIsSelect(this, this.condition, this.data, this.$refs.table, this.selectRows);
checkTableRowIsSelect(
this,
this.condition,
this.data,
this.$refs.table,
this.selectRows
);
},
clearSelection() {
this.clearSelectRows();
@ -494,7 +536,10 @@ export default {
this.$refs.customTableHeader.open(this.fields);
},
resetHeader() {
this.$emit('update:fields', getCustomTableHeader(this.fieldKey, this.customFields));
this.$emit(
"update:fields",
getCustomTableHeader(this.fieldKey, this.customFields)
);
this.tableActive = false;
this.$nextTick(() => {
this.doLayout();
@ -510,30 +555,32 @@ export default {
this.doLayout();
});
},
addPaddingColClass({column}) {
if (column.columnKey === 'tableRowDropCol'
|| column.columnKey === 'selectionCol'
|| column.columnKey === 'batchBtnCol') {
return 'padding-col';
addPaddingColClass({ column }) {
if (
column.columnKey === "tableRowDropCol" ||
column.columnKey === "selectionCol" ||
column.columnKey === "batchBtnCol"
) {
return "padding-col";
}
},
rowStyle({row}) {
return row.hidden ? {"display": "none"} : {};
rowStyle({ row }) {
return row.hidden ? { display: "none" } : {};
},
tableRowClassName(row) {
if (row.row.hidden) {
return 'ms-variable-hidden-row';
return "ms-variable-hidden-row";
}
return '';
return "";
},
}
},
};
</script>
<style scoped>
.batch-popper {
top: 300px;
color: #1FDD02;
color: #1fdd02;
}
.el-table :deep(.padding-col) .cell {
@ -562,7 +609,10 @@ export default {
}
.ms-table :deep(.el-table__body) tr.hover-row.current-row > td,
.ms-table :deep(.el-table__body) tr.hover-row.el-table__row--striped.current-row > td,
.ms-table
:deep(.el-table__body)
tr.hover-row.el-table__row--striped.current-row
> td,
.ms-table :deep(.el-table__body) tr.hover-row.el-table__row--striped > td,
.ms-table :deep(.el-table__body) tr.hover-row > td {
background-color: #ffffff;
@ -570,10 +620,10 @@ export default {
/* 解决拖拽排序后hover阴影错乱问题 */
.ms-table :deep(.el-table__body) tr:hover > td {
background-color: #F5F7FA;
background-color: #f5f7fa;
}
.disable-hover :deep(tr:hover>td) {
.disable-hover :deep(tr:hover > td) {
background-color: #ffffff !important;
}

View File

@ -1,6 +1,6 @@
import i18n from '../i18n'
import {getCurrentUserId} from "../utils/token";
import {SYSTEM_FIELD_NAME_MAP} from "../utils/table-constants";
import i18n from "../i18n";
import { getCurrentUserId } from "../utils/token";
import { SYSTEM_FIELD_NAME_MAP } from "../utils/table-constants";
function setDefaultValue(item, value) {
item.defaultValue = value;
@ -25,8 +25,7 @@ export function parseCustomField(data, template, rules, oldFields) {
let customFieldForm = {};
// 设置页面显示的默认值
template.customFields.forEach(item => {
template.customFields.forEach((item) => {
if (item.defaultValue && !item.hasParse) {
let val = item.defaultValue;
try {
@ -34,7 +33,12 @@ export function parseCustomField(data, template, rules, oldFields) {
} catch (e) {
//
}
if (item.name === '责任人' && item.system && val && val === 'CURRENT_USER') {
if (
item.name === "责任人" &&
item.system &&
val &&
val === "CURRENT_USER"
) {
val = getCurrentUserId();
}
setDefaultValue(item, val);
@ -42,11 +46,11 @@ export function parseCustomField(data, template, rules, oldFields) {
// 添加自定义字段必填校验
if (item.required) {
let msg = (item.system ? i18n.t(SYSTEM_FIELD_NAME_MAP[item.name]) : item.name) + i18n.t('commons.cannot_be_null');
let msg =
(item.system ? i18n.t(SYSTEM_FIELD_NAME_MAP[item.name]) : item.name) +
i18n.t("commons.cannot_be_null");
if (rules) {
rules[item.name] = [
{required: true, message: msg, trigger: 'blur'}
];
rules[item.name] = [{ required: true, message: msg, trigger: "blur" }];
}
}
@ -67,7 +71,7 @@ export function parseCustomField(data, template, rules, oldFields) {
let customField = data.fields[i];
if (customField.id === item.id) {
try {
if (item.type === 'textarea' || item.type === 'richText') {
if (item.type === "textarea" || item.type === "richText") {
setDefaultValue(item, customField.textValue);
} else {
setDefaultValue(item, customField.value);
@ -79,7 +83,8 @@ export function parseCustomField(data, template, rules, oldFields) {
break;
}
}
} else if (data.fields instanceof Object) { // todo
} else if (data.fields instanceof Object) {
// todo
// 兼容旧的存储方式
for (const key in data.fields) {
if (item.name === key) {
@ -107,27 +112,31 @@ export function buildCustomFields(data, param, template) {
let editFields = [];
let requestFields = [];
template.customFields.forEach(item => {
let customField = {fieldId: item.id};
if (['richText', 'textarea'].indexOf(item.type) > -1) {
customField['textValue'] = item.defaultValue;
template.customFields.forEach((item) => {
let customField = { fieldId: item.id };
if (["richText", "textarea"].indexOf(item.type) > -1) {
customField["textValue"] = item.defaultValue;
} else {
customField['value'] = item.defaultValue ? JSON.stringify(item.defaultValue): "";
customField["value"] = item.defaultValue
? JSON.stringify(item.defaultValue)
: "";
}
if (item.isEdit) {
editFields.push(customField);
} else {
addFields.push(customField);
}
let fieldValue = (item.defaultValue instanceof Array && item.type !== 'multipleInput') ?
JSON.stringify(item.defaultValue) : (item.defaultValue || "");
let fieldValue =
item.defaultValue instanceof Array && item.type !== "multipleInput"
? JSON.stringify(item.defaultValue)
: item.defaultValue || "";
let requestField = {
id: item.id,
name: item.name,
customData: item.customData,
type: item.type,
value: fieldValue
}
value: fieldValue,
};
requestFields.push(requestField);
});
param.addFields = addFields;
@ -142,7 +151,7 @@ export function getTemplate(baseUrl, vueObj) {
vueObj.$get(baseUrl + vueObj.projectId, (response) => {
template = response.data;
if (template.customFields) {
template.customFields.forEach(item => {
template.customFields.forEach((item) => {
if (item.options) {
item.options = JSON.parse(item.options);
}
@ -156,28 +165,64 @@ export function getTemplate(baseUrl, vueObj) {
// 兼容旧字段
export function buildTestCaseOldFields(testCase) {
let oldFields = new Map();
oldFields.set('用例状态', testCase.status);
oldFields.set('责任人', testCase.maintainer);
oldFields.set('用例等级', testCase.priority);
oldFields.set("用例状态", testCase.status);
oldFields.set("责任人", testCase.maintainer);
oldFields.set("用例等级", testCase.priority);
return oldFields;
}
export function sortCustomFields(customFields) {
let total = 0;//定义total用于控制循环结束
let total = 0; //定义total用于控制循环结束
for (let i = 0; total < customFields.length; total++) {
if (typeof (customFields[i].defaultValue) === 'string' || customFields[i].defaultValue instanceof String) {
if (
typeof customFields[i].defaultValue === "string" ||
customFields[i].defaultValue instanceof String
) {
try {
customFields[i].defaultValue = JSON.parse(customFields[i].defaultValue);
} catch (e) {
// nothing
}
}
if (customFields[i].type === 'richText') {
if (customFields[i].type === "richText") {
//循环到是0的位置就删除该元素0并且在arr末尾push进这个元素0由于splice删除了该位置元素所以i不用+1下次循环仍然检查i位置的元素
customFields.push(customFields.splice(i, 1)[0]);
} else {
i++;//循环到不是0的位置就继续往后循环
i++; //循环到不是0的位置就继续往后循环
}
}
return customFields;
}
export function getApiParamsConfigFields(_this) {
return [
{ value: "MIX_LENGTH", text: _this.$t("schema.minLength") },
{ value: "MAX_LENGTH", text: _this.$t("schema.maxLength") },
{ value: "ENCODE", text: _this.$t("commons.encode") },
{ value: "DESCRIPTION", text: _this.$t("commons.description") },
];
}
export function getApiJsonSchemaConfigFields(_this) {
return [
{ value: "MIX_LENGTH", text: _this.$t("schema.minLength") },
{ value: "MAX_LENGTH", text: _this.$t("schema.maxLength") },
{ value: "DEFAULT", text: _this.$t("commons.default") },
{ value: "ENUM", text: _this.$t("schema.enum") },
{ value: "PATTERN", text: _this.$t("schema.pattern") },
{ value: "FORMAT", text: _this.$t("schema.format") },
{ value: "DESCRIPTION", text: _this.$t("commons.description") },
];
}
export function getShowFields(key) {
let apiParamsShowFields = localStorage.getItem(key);
if (apiParamsShowFields) {
try {
return JSON.parse(apiParamsShowFields);
} catch (e) {
return [];
}
}
return [];
}