feat(接口测试): csv参数表格&mock 调整
This commit is contained in:
parent
5784ac55a0
commit
dd97da9e43
|
@ -93,7 +93,7 @@
|
||||||
class="m-0 border-none p-0"
|
class="m-0 border-none p-0"
|
||||||
:self-style="{ backgroundColor: 'transparent !important' }"
|
:self-style="{ backgroundColor: 'transparent !important' }"
|
||||||
:closable="data.value !== '__arco__more' && !props.disabled"
|
:closable="data.value !== '__arco__more' && !props.disabled"
|
||||||
@close="handleClose(data)"
|
@close="() => handleClose(data)"
|
||||||
>
|
>
|
||||||
{{ data.value === '__arco__more' ? data.label.replace('...', '') : data.label }}
|
{{ data.value === '__arco__more' ? data.label.replace('...', '') : data.label }}
|
||||||
</MsTag>
|
</MsTag>
|
||||||
|
|
|
@ -121,7 +121,7 @@
|
||||||
{
|
{
|
||||||
title: 'project.fileManagement.type',
|
title: 'project.fileManagement.type',
|
||||||
dataIndex: 'fileType',
|
dataIndex: 'fileType',
|
||||||
width: 90,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'project.fileManagement.size',
|
title: 'project.fileManagement.size',
|
||||||
|
|
|
@ -148,8 +148,11 @@
|
||||||
personalMenusVisible.value = false;
|
personalMenusVisible.value = false;
|
||||||
orgKeyword.value = '';
|
orgKeyword.value = '';
|
||||||
await userStore.isLogin(true);
|
await userStore.isLogin(true);
|
||||||
if (!appStore.currentProjectId || appStore.currentProjectId === 'no_such_project') {
|
if (
|
||||||
// 没有项目权限(组织没有项目, 或项目全被禁用)
|
(!appStore.currentProjectId || appStore.currentProjectId === 'no_such_project') &&
|
||||||
|
!(route.name as string).startsWith(SettingRouteEnum.SETTING)
|
||||||
|
) {
|
||||||
|
// 没有项目权限(组织没有项目, 或项目全被禁用)且访问的页面非系统菜单模块,则重定向到无项目权限页面
|
||||||
router.push({
|
router.push({
|
||||||
name: NO_PROJECT_ROUTE_NAME,
|
name: NO_PROJECT_ROUTE_NAME,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<MsMinderEditor
|
<MsMinderEditor
|
||||||
|
v-model:activeExtraKey="activeExtraKey"
|
||||||
:tags="tags"
|
:tags="tags"
|
||||||
:import-json="props.importJson"
|
:import-json="props.importJson"
|
||||||
:replaceable-tags="replaceableTags"
|
:replaceable-tags="replaceableTags"
|
||||||
:insert-node="insertNode"
|
:insert-node="insertNode"
|
||||||
:priority-disable-check="priorityDisableCheck"
|
:priority-disable-check="priorityDisableCheck"
|
||||||
:after-tag-edit="afterTagEdit"
|
:after-tag-edit="afterTagEdit"
|
||||||
|
:extract-content-tab-list="extractContentTabList"
|
||||||
single-tag
|
single-tag
|
||||||
tag-enable
|
tag-enable
|
||||||
sequence-enable
|
sequence-enable
|
||||||
@click="handleNodeClick"
|
@click="handleNodeClick"
|
||||||
>
|
/>
|
||||||
</MsMinderEditor>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
@ -34,6 +35,25 @@
|
||||||
const tags = [...topTags, t('ms.minders.precondition'), ...descTags, t('ms.minders.stepExpect'), t('common.remark')];
|
const tags = [...topTags, t('ms.minders.precondition'), ...descTags, t('ms.minders.stepExpect'), t('common.remark')];
|
||||||
const visible = ref<boolean>(false);
|
const visible = ref<boolean>(false);
|
||||||
const nodeData = ref<any>({});
|
const nodeData = ref<any>({});
|
||||||
|
const extractContentTabList = [
|
||||||
|
{
|
||||||
|
label: t('common.baseInfo'),
|
||||||
|
value: 'baseInfo',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('caseManagement.featureCase.attachment'),
|
||||||
|
value: 'attachment',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'comments',
|
||||||
|
label: t('caseManagement.featureCase.comments'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'bug',
|
||||||
|
label: t('caseManagement.featureCase.bug'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const activeExtraKey = ref('baseInfo');
|
||||||
|
|
||||||
function handleNodeClick(data: any) {
|
function handleNodeClick(data: any) {
|
||||||
if (data.resource && data.resource.includes(caseTag)) {
|
if (data.resource && data.resource.includes(caseTag)) {
|
||||||
|
|
|
@ -1,58 +1,64 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="ms-minder-editor-container">
|
<div class="ms-minder-editor-container">
|
||||||
<minderHeader
|
<div class="flex-1">
|
||||||
:sequence-enable="props.sequenceEnable"
|
<minderHeader
|
||||||
:tag-enable="props.tagEnable"
|
:sequence-enable="props.sequenceEnable"
|
||||||
:progress-enable="props.progressEnable"
|
:tag-enable="props.tagEnable"
|
||||||
:priority-count="props.priorityCount"
|
:progress-enable="props.progressEnable"
|
||||||
:priority-prefix="props.priorityPrefix"
|
:priority-count="props.priorityCount"
|
||||||
:priority-start-with-zero="props.priorityStartWithZero"
|
:priority-prefix="props.priorityPrefix"
|
||||||
:tags="props.tags"
|
:priority-start-with-zero="props.priorityStartWithZero"
|
||||||
:move-enable="props.moveEnable"
|
:tags="props.tags"
|
||||||
:move-confirm="props.moveConfirm"
|
:move-enable="props.moveEnable"
|
||||||
:tag-edit-check="props.tagEditCheck"
|
:move-confirm="props.moveConfirm"
|
||||||
:tag-disable-check="props.tagDisableCheck"
|
:tag-edit-check="props.tagEditCheck"
|
||||||
:priority-disable-check="props.priorityDisableCheck"
|
:tag-disable-check="props.tagDisableCheck"
|
||||||
:distinct-tags="props.distinctTags"
|
:priority-disable-check="props.priorityDisableCheck"
|
||||||
:default-mold="props.defaultMold"
|
:distinct-tags="props.distinctTags"
|
||||||
:del-confirm="props.delConfirm"
|
:default-mold="props.defaultMold"
|
||||||
:arrange-enable="props.arrangeEnable"
|
:del-confirm="props.delConfirm"
|
||||||
:mold-enable="props.moldEnable"
|
:arrange-enable="props.arrangeEnable"
|
||||||
:font-enable="props.fontEnable"
|
:mold-enable="props.moldEnable"
|
||||||
:style-enable="props.styleEnable"
|
:font-enable="props.fontEnable"
|
||||||
:replaceable-tags="props.replaceableTags"
|
:style-enable="props.styleEnable"
|
||||||
:single-tag="props.singleTag"
|
:replaceable-tags="props.replaceableTags"
|
||||||
:insert-node="props.insertNode"
|
:single-tag="props.singleTag"
|
||||||
:after-tag-edit="props.afterTagEdit"
|
:insert-node="props.insertNode"
|
||||||
@mold-change="handleMoldChange"
|
:after-tag-edit="props.afterTagEdit"
|
||||||
/>
|
@mold-change="handleMoldChange"
|
||||||
<mainEditor
|
/>
|
||||||
:disabled="props.disabled"
|
<mainEditor
|
||||||
:sequence-enable="props.sequenceEnable"
|
:disabled="props.disabled"
|
||||||
:tag-enable="props.tagEnable"
|
:sequence-enable="props.sequenceEnable"
|
||||||
:move-enable="props.moveEnable"
|
:tag-enable="props.tagEnable"
|
||||||
:move-confirm="props.moveConfirm"
|
:move-enable="props.moveEnable"
|
||||||
:progress-enable="props.progressEnable"
|
:move-confirm="props.moveConfirm"
|
||||||
:import-json="props.importJson"
|
:progress-enable="props.progressEnable"
|
||||||
:height="props.height"
|
:import-json="props.importJson"
|
||||||
:tags="props.tags"
|
:height="props.height"
|
||||||
:distinct-tags="props.distinctTags"
|
:tags="props.tags"
|
||||||
:tag-edit-check="props.tagEditCheck"
|
:distinct-tags="props.distinctTags"
|
||||||
:tag-disable-check="props.tagDisableCheck"
|
:tag-edit-check="props.tagEditCheck"
|
||||||
:priority-count="props.priorityCount"
|
:tag-disable-check="props.tagDisableCheck"
|
||||||
:priority-prefix="props.priorityPrefix"
|
:priority-count="props.priorityCount"
|
||||||
:priority-start-with-zero="props.priorityStartWithZero"
|
:priority-prefix="props.priorityPrefix"
|
||||||
:insert-node="props.insertNode"
|
:priority-start-with-zero="props.priorityStartWithZero"
|
||||||
@after-mount="emit('afterMount')"
|
:insert-node="props.insertNode"
|
||||||
@save="save"
|
@after-mount="() => emit('afterMount')"
|
||||||
@enter-node="handleEnterNode"
|
@save="save"
|
||||||
/>
|
@enter-node="handleEnterNode"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="props.extractContentTabList?.length" class="ms-minder-editor-extra-content">
|
||||||
|
<MsTab v-model:activeKey="activeExtraKey" :content-tab-list="props.extractContentTabList" mode="button" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" name="minderEditor" setup>
|
<script lang="ts" name="minderEditor" setup>
|
||||||
import { onMounted } from 'vue';
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
|
import MsTab from '@/components/pure/ms-tab/index.vue';
|
||||||
import minderHeader from './main/header.vue';
|
import minderHeader from './main/header.vue';
|
||||||
import mainEditor from './main/mainEditor.vue';
|
import mainEditor from './main/mainEditor.vue';
|
||||||
|
|
||||||
|
@ -104,6 +110,10 @@
|
||||||
emit('enterNode', data);
|
emit('enterNode', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeExtraKey = defineModel<string>('activeExtraKey', {
|
||||||
|
default: '',
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (window.minder.on) {
|
if (window.minder.on) {
|
||||||
|
@ -123,6 +133,14 @@
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.ms-minder-editor-container {
|
.ms-minder-editor-container {
|
||||||
@apply relative h-full;
|
@apply relative flex h-full;
|
||||||
|
.ms-minder-editor-extra-content {
|
||||||
|
@apply border-l;
|
||||||
|
|
||||||
|
padding: 16px;
|
||||||
|
width: 35%;
|
||||||
|
min-width: 360px;
|
||||||
|
border-color: var(--color-text-n8);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -44,6 +44,7 @@ export const mainEditorProps = {
|
||||||
default: 500,
|
default: 500,
|
||||||
},
|
},
|
||||||
disabled: Boolean,
|
disabled: Boolean,
|
||||||
|
extractContentTabList: [] as PropType<{ label: string; value: string }[]>,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const headerProps = {
|
export const headerProps = {
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<a-tabs v-model:active-key="innerActiveKey" :class="[props.class, props.noContent ? 'no-content' : '']">
|
<a-tabs
|
||||||
|
v-if="props.mode === 'origin'"
|
||||||
|
v-model:active-key="innerActiveKey"
|
||||||
|
:class="[props.class, props.noContent ? 'no-content' : '']"
|
||||||
|
>
|
||||||
<a-tab-pane v-for="item of props.contentTabList" :key="item.value" :title="item.label">
|
<a-tab-pane v-for="item of props.contentTabList" :key="item.value" :title="item.label">
|
||||||
<template v-if="props.showBadge" #title>
|
<template v-if="props.showBadge" #title>
|
||||||
<a-badge
|
<a-badge
|
||||||
|
@ -18,11 +22,23 @@
|
||||||
</template>
|
</template>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
|
<div v-else class="ms-tab--button">
|
||||||
|
<div
|
||||||
|
v-for="item of props.contentTabList"
|
||||||
|
:key="item.value"
|
||||||
|
class="ms-tab--button-item"
|
||||||
|
:class="item.value === innerActiveKey ? 'ms-tab--button-item--active' : ''"
|
||||||
|
@click="innerActiveKey = item.value"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
|
mode?: 'origin' | 'button';
|
||||||
activeKey: string;
|
activeKey: string;
|
||||||
contentTabList: { label: string; value: string }[];
|
contentTabList: { label: string; value: string }[];
|
||||||
class?: string;
|
class?: string;
|
||||||
|
@ -31,6 +47,7 @@
|
||||||
showBadge?: boolean;
|
showBadge?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
|
mode: 'origin',
|
||||||
showBadge: true,
|
showBadge: true,
|
||||||
getTextFunc: (value: any) => value,
|
getTextFunc: (value: any) => value,
|
||||||
class: '',
|
class: '',
|
||||||
|
@ -63,4 +80,35 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.ms-tab--button {
|
||||||
|
@apply flex;
|
||||||
|
|
||||||
|
border-radius: var(--border-radius-small);
|
||||||
|
.ms-tab--button-item {
|
||||||
|
@apply cursor-pointer;
|
||||||
|
|
||||||
|
padding: 4px 12px;
|
||||||
|
border: 1px solid var(--color-text-n8);
|
||||||
|
color: var(--color-text-2);
|
||||||
|
&:first-child {
|
||||||
|
border-top-left-radius: var(--border-radius-small);
|
||||||
|
border-bottom-left-radius: var(--border-radius-small);
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
border-top-right-radius: var(--border-radius-small);
|
||||||
|
border-bottom-right-radius: var(--border-radius-small);
|
||||||
|
}
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: -1px;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: rgb(var(--primary-5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ms-tab--button-item--active {
|
||||||
|
z-index: 2;
|
||||||
|
border: 1px solid rgb(var(--primary-5)) !important;
|
||||||
|
color: rgb(var(--primary-5));
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -247,7 +247,7 @@
|
||||||
wrapper-id="ms-table-footer-wrapper"
|
wrapper-id="ms-table-footer-wrapper"
|
||||||
:size="props.paginationSize"
|
:size="props.paginationSize"
|
||||||
@batch-action="handleBatchAction"
|
@batch-action="handleBatchAction"
|
||||||
@clear="emit('clearSelector')"
|
@clear="() => emit('clearSelector')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ms-pagination
|
<ms-pagination
|
||||||
|
@ -269,7 +269,7 @@
|
||||||
:show-subdirectory="!!attrs.showSubdirectory"
|
:show-subdirectory="!!attrs.showSubdirectory"
|
||||||
@init-data="handleInitColumn"
|
@init-data="handleInitColumn"
|
||||||
@page-size-change="pageSizeChange"
|
@page-size-change="pageSizeChange"
|
||||||
@module-change="emit('moduleChange')"
|
@module-change="() => emit('moduleChange')"
|
||||||
></ColumnSelector>
|
></ColumnSelector>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -234,7 +234,7 @@ export interface AssertionConfig {
|
||||||
assertions: ExecuteAssertionItem[];
|
assertions: ExecuteAssertionItem[];
|
||||||
}
|
}
|
||||||
export interface CsvVariable {
|
export interface CsvVariable {
|
||||||
id: string;
|
id?: string;
|
||||||
fileId: string;
|
fileId: string;
|
||||||
scenarioId: string;
|
scenarioId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -250,6 +250,8 @@ export interface CsvVariable {
|
||||||
allowQuotedData: boolean;
|
allowQuotedData: boolean;
|
||||||
recycleOnEof: boolean;
|
recycleOnEof: boolean;
|
||||||
stopThreadOnEof: boolean;
|
stopThreadOnEof: boolean;
|
||||||
|
// 以下为前端字段
|
||||||
|
settingVisible: boolean;
|
||||||
}
|
}
|
||||||
export interface CommonVariable {
|
export interface CommonVariable {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
|
|
|
@ -297,7 +297,7 @@ export const mockDefaultParams: MockParams = {
|
||||||
matchAll: true,
|
matchAll: true,
|
||||||
},
|
},
|
||||||
body: {
|
body: {
|
||||||
bodyType: RequestBodyFormat.FORM_DATA,
|
bodyType: RequestBodyFormat.NONE,
|
||||||
formDataBody: {
|
formDataBody: {
|
||||||
matchRules: [],
|
matchRules: [],
|
||||||
matchAll: true,
|
matchAll: true,
|
||||||
|
@ -348,7 +348,7 @@ export const mockDefaultParams: MockParams = {
|
||||||
uploadFileIds: [],
|
uploadFileIds: [],
|
||||||
linkFileIds: [],
|
linkFileIds: [],
|
||||||
};
|
};
|
||||||
export const makeDefaultParams = () => {
|
export const makeMockDefaultParams = () => {
|
||||||
const defaultParams = cloneDeep(mockDefaultParams);
|
const defaultParams = cloneDeep(mockDefaultParams);
|
||||||
defaultParams.id = Date.now().toString();
|
defaultParams.id = Date.now().toString();
|
||||||
defaultParams.mockMatchRule.body.formDataBody.matchRules.push({
|
defaultParams.mockMatchRule.body.formDataBody.matchRules.push({
|
||||||
|
@ -426,7 +426,6 @@ export const defaultNormalParamItem = {
|
||||||
};
|
};
|
||||||
// 场景-csv参数默认值
|
// 场景-csv参数默认值
|
||||||
export const defaultCsvParamItem: CsvVariable = {
|
export const defaultCsvParamItem: CsvVariable = {
|
||||||
id: '',
|
|
||||||
fileId: '',
|
fileId: '',
|
||||||
scenarioId: '',
|
scenarioId: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
@ -442,4 +441,5 @@ export const defaultCsvParamItem: CsvVariable = {
|
||||||
allowQuotedData: false,
|
allowQuotedData: false,
|
||||||
recycleOnEof: false,
|
recycleOnEof: false,
|
||||||
stopThreadOnEof: false,
|
stopThreadOnEof: false,
|
||||||
|
settingVisible: false,
|
||||||
};
|
};
|
||||||
|
|
|
@ -125,6 +125,30 @@
|
||||||
/>
|
/>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</template>
|
</template>
|
||||||
|
<template #name="{ record, columnConfig, rowIndex }">
|
||||||
|
<a-popover
|
||||||
|
position="tl"
|
||||||
|
:disabled="!record[columnConfig.dataIndex as string] || record[columnConfig.dataIndex as string].trim() === ''"
|
||||||
|
class="ms-params-input-popover"
|
||||||
|
>
|
||||||
|
<template #content>
|
||||||
|
<div class="param-popover-title">
|
||||||
|
{{ t('apiTestDebug.paramName') }}
|
||||||
|
</div>
|
||||||
|
<div class="param-popover-value">
|
||||||
|
{{ record[columnConfig.dataIndex as string] }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<a-input
|
||||||
|
v-model:model-value="record[columnConfig.dataIndex as string]"
|
||||||
|
:disabled="props.disabledExceptParam || columnConfig.disabledColumn"
|
||||||
|
:placeholder="t('apiTestDebug.commonPlaceholder')"
|
||||||
|
class="ms-form-table-input"
|
||||||
|
size="mini"
|
||||||
|
@input="() => addTableLine(rowIndex, columnConfig.addLineDisabled)"
|
||||||
|
/>
|
||||||
|
</a-popover>
|
||||||
|
</template>
|
||||||
<!-- 参数类型 -->
|
<!-- 参数类型 -->
|
||||||
<template #paramType="{ record, columnConfig, rowIndex }">
|
<template #paramType="{ record, columnConfig, rowIndex }">
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
|
@ -242,7 +266,7 @@
|
||||||
input-class="ms-form-table-input h-[24px]"
|
input-class="ms-form-table-input h-[24px]"
|
||||||
input-size="small"
|
input-size="small"
|
||||||
tag-size="small"
|
tag-size="small"
|
||||||
@change="(files, file) => handleFileChange(files, record, rowIndex, file)"
|
@change="(files, file) => handleFilesChange(files, record, rowIndex, file)"
|
||||||
@delete-file="() => emitChange('deleteFile')"
|
@delete-file="() => emitChange('deleteFile')"
|
||||||
/>
|
/>
|
||||||
<MsParamsInput
|
<MsParamsInput
|
||||||
|
@ -251,14 +275,14 @@
|
||||||
:disabled="props.disabledParamValue"
|
:disabled="props.disabledParamValue"
|
||||||
size="mini"
|
size="mini"
|
||||||
@change="() => addTableLine(rowIndex, columnConfig.addLineDisabled)"
|
@change="() => addTableLine(rowIndex, columnConfig.addLineDisabled)"
|
||||||
@dblclick="quickInputParams(record)"
|
@dblclick="() => quickInputParams(record)"
|
||||||
@apply="() => addTableLine(rowIndex, columnConfig.addLineDisabled)"
|
@apply="() => addTableLine(rowIndex, columnConfig.addLineDisabled)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<!-- 文件 -->
|
<!-- 文件 -->
|
||||||
<template #file="{ record, rowIndex }">
|
<template #file="{ record, rowIndex }">
|
||||||
<MsAddAttachment
|
<MsAddAttachment
|
||||||
v-model:file-list="record.files"
|
:file-list="[record]"
|
||||||
:disabled="props.disabledParamValue"
|
:disabled="props.disabledParamValue"
|
||||||
:multiple="false"
|
:multiple="false"
|
||||||
mode="input"
|
mode="input"
|
||||||
|
@ -321,7 +345,7 @@
|
||||||
:disabled="props.disabledExceptParam || columnConfig.disabledColumn"
|
:disabled="props.disabledExceptParam || columnConfig.disabledColumn"
|
||||||
size="mini"
|
size="mini"
|
||||||
@input="() => addTableLine(rowIndex)"
|
@input="() => addTableLine(rowIndex)"
|
||||||
@dblclick="quickInputDesc(record)"
|
@dblclick="() => quickInputDesc(record)"
|
||||||
@change="handleDescChange"
|
@change="handleDescChange"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
@ -464,7 +488,7 @@
|
||||||
v-if="Array.isArray(record.domain)"
|
v-if="Array.isArray(record.domain)"
|
||||||
:tag-list="getDomain(record.domain)"
|
:tag-list="getDomain(record.domain)"
|
||||||
size="small"
|
size="small"
|
||||||
@click="showHostModal(record)"
|
@click="() => showHostModal(record)"
|
||||||
/>
|
/>
|
||||||
<div v-else class="text-[var(--color-text-1)]">{{ '-' }}</div>
|
<div v-else class="text-[var(--color-text-1)]">{{ '-' }}</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -924,7 +948,10 @@
|
||||||
emitChange('toggleRequired');
|
emitChange('toggleRequired');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleFileChange(
|
/**
|
||||||
|
* 处理表格内多个文件上传/关联
|
||||||
|
*/
|
||||||
|
async function handleFilesChange(
|
||||||
files: MsFileItem[],
|
files: MsFileItem[],
|
||||||
record: Record<string, any>,
|
record: Record<string, any>,
|
||||||
rowIndex: number,
|
rowIndex: number,
|
||||||
|
@ -957,6 +984,45 @@
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
addTableLine(rowIndex);
|
addTableLine(rowIndex);
|
||||||
|
emitChange('handleFilesChange');
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
appStore.hideLoading();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理表格内单个文件上传/关联
|
||||||
|
*/
|
||||||
|
async function handleFileChange(
|
||||||
|
files: MsFileItem[],
|
||||||
|
record: Record<string, any>,
|
||||||
|
rowIndex: number,
|
||||||
|
file?: MsFileItem
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
if (props.uploadTempFileApi && file?.local) {
|
||||||
|
appStore.showLoading();
|
||||||
|
const res = await props.uploadTempFileApi(file.file);
|
||||||
|
record = {
|
||||||
|
...record,
|
||||||
|
...file,
|
||||||
|
fileId: res.data,
|
||||||
|
fileName: file.name || '',
|
||||||
|
fileAlias: file.name || '',
|
||||||
|
};
|
||||||
|
} else if (file) {
|
||||||
|
record = {
|
||||||
|
...record,
|
||||||
|
...file,
|
||||||
|
fileId: file.uid || file.fileId || '',
|
||||||
|
fileName: file.originalName || '',
|
||||||
|
fileAlias: file.name || '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
addTableLine(rowIndex);
|
||||||
emitChange('handleFileChange');
|
emitChange('handleFileChange');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
:default-param-item="defaultHeaderParamsItem"
|
:default-param-item="defaultHeaderParamsItem"
|
||||||
:draggable="!props.disabledExceptParam"
|
:draggable="!props.disabledExceptParam"
|
||||||
@change="handleParamTableChange"
|
@change="handleParamTableChange"
|
||||||
@batch-add="batchAddKeyValVisible = true"
|
@batch-add="() => (batchAddKeyValVisible = true)"
|
||||||
/>
|
/>
|
||||||
<batchAddKeyVal
|
<batchAddKeyVal
|
||||||
v-model:visible="batchAddKeyValVisible"
|
v-model:visible="batchAddKeyValVisible"
|
||||||
|
|
|
@ -281,7 +281,7 @@
|
||||||
async function initModuleCount() {
|
async function initModuleCount() {
|
||||||
try {
|
try {
|
||||||
const res = await getDebugModuleCount({
|
const res = await getDebugModuleCount({
|
||||||
keyword: moduleKeyword.value,
|
keyword: '',
|
||||||
});
|
});
|
||||||
modulesCount.value = res;
|
modulesCount.value = res;
|
||||||
folderTree.value = mapTree<ModuleTreeNode>(folderTree.value, (node) => {
|
folderTree.value = mapTree<ModuleTreeNode>(folderTree.value, (node) => {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
@open-api-tab="(record, isExecute) => openApiTab({ apiInfo: record, isCopy: false, isExecute })"
|
@open-api-tab="(record, isExecute) => openApiTab({ apiInfo: record, isCopy: false, isExecute })"
|
||||||
@open-copy-api-tab="openApiTab({ apiInfo: $event, isCopy: true })"
|
@open-copy-api-tab="openApiTab({ apiInfo: $event, isCopy: true })"
|
||||||
@add-api-tab="addApiTab"
|
@add-api-tab="addApiTab"
|
||||||
@import="emit('import')"
|
@import="() => emit('import')"
|
||||||
@open-edit-api-tab="openApiTab"
|
@open-edit-api-tab="openApiTab"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -156,6 +156,7 @@
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import {
|
import {
|
||||||
RequestAuthType,
|
RequestAuthType,
|
||||||
|
RequestBodyFormat,
|
||||||
RequestComposition,
|
RequestComposition,
|
||||||
RequestDefinitionStatus,
|
RequestDefinitionStatus,
|
||||||
RequestMethods,
|
RequestMethods,
|
||||||
|
@ -348,6 +349,7 @@
|
||||||
loadedApiTab = {
|
loadedApiTab = {
|
||||||
...loadedApiTab,
|
...loadedApiTab,
|
||||||
...(apiInfo as ApiDefinitionDetail).request,
|
...(apiInfo as ApiDefinitionDetail).request,
|
||||||
|
activeTab: (apiInfo as ApiDefinitionDetail).activeTab,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// 如果点击的请求在tab中已经存在,则直接切换到该tab
|
// 如果点击的请求在tab中已经存在,则直接切换到该tab
|
||||||
|
@ -385,6 +387,7 @@
|
||||||
request = {
|
request = {
|
||||||
...res.request,
|
...res.request,
|
||||||
...(apiInfo as ApiDefinitionDetail).request,
|
...(apiInfo as ApiDefinitionDetail).request,
|
||||||
|
activeTab: (apiInfo as ApiDefinitionDetail).activeTab,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
addApiTab({
|
addApiTab({
|
||||||
|
@ -417,6 +420,16 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openApiTabAndDebugMock(mock: MockDetail) {
|
async function openApiTabAndDebugMock(mock: MockDetail) {
|
||||||
|
let activeTab = RequestComposition.BODY;
|
||||||
|
if (mock.mockMatchRule.body.bodyType !== RequestBodyFormat.NONE) {
|
||||||
|
activeTab = RequestComposition.BODY;
|
||||||
|
} else if (mock.mockMatchRule.header.matchRules.length > 0) {
|
||||||
|
activeTab = RequestComposition.HEADER;
|
||||||
|
} else if (mock.mockMatchRule.query.matchRules.length > 0) {
|
||||||
|
activeTab = RequestComposition.QUERY;
|
||||||
|
} else if (mock.mockMatchRule.rest.matchRules.length > 0) {
|
||||||
|
activeTab = RequestComposition.REST;
|
||||||
|
}
|
||||||
openApiTab({
|
openApiTab({
|
||||||
apiInfo: {
|
apiInfo: {
|
||||||
id: mock.apiDefinitionId as string,
|
id: mock.apiDefinitionId as string,
|
||||||
|
@ -465,6 +478,7 @@
|
||||||
value: e.value,
|
value: e.value,
|
||||||
})) || [],
|
})) || [],
|
||||||
},
|
},
|
||||||
|
activeTab,
|
||||||
} as unknown as ApiDefinitionDetail,
|
} as unknown as ApiDefinitionDetail,
|
||||||
isExecute: true,
|
isExecute: true,
|
||||||
isDebugMock: true,
|
isDebugMock: true,
|
||||||
|
|
|
@ -351,14 +351,12 @@
|
||||||
:deep(.arco-tabs-content) {
|
:deep(.arco-tabs-content) {
|
||||||
@apply flex-1 pt-0;
|
@apply flex-1 pt-0;
|
||||||
.arco-tabs-content-item {
|
.arco-tabs-content-item {
|
||||||
@apply px-0;
|
@apply overflow-y-auto px-0;
|
||||||
|
.ms-scroll-bar();
|
||||||
}
|
}
|
||||||
.arco-tabs-content-list {
|
.arco-tabs-content-list {
|
||||||
@apply h-full;
|
@apply h-full;
|
||||||
}
|
}
|
||||||
.arco-tabs-content-list .arco-tabs-content-item:nth-of-type(1) .arco-tabs-pane {
|
|
||||||
@apply h-full overflow-hidden;
|
|
||||||
}
|
|
||||||
.arco-collapse {
|
.arco-collapse {
|
||||||
height: calc(100% - 85px);
|
height: calc(100% - 85px);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MsDetailCard>
|
</MsDetailCard>
|
||||||
<a-form ref="mockForm" :model="mockDetail" :disabled="isReadOnly">
|
<a-form ref="mockFormRef" :model="mockDetail" :disabled="isReadOnly">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
class="hidden-item mb-[16px]"
|
class="hidden-item mb-[16px]"
|
||||||
field="name"
|
field="name"
|
||||||
|
@ -196,7 +196,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Message } from '@arco-design/web-vue';
|
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
|
@ -234,7 +234,7 @@
|
||||||
defaultHeaderParamsItem,
|
defaultHeaderParamsItem,
|
||||||
defaultMatchRuleItem,
|
defaultMatchRuleItem,
|
||||||
defaultRequestParamsItem,
|
defaultRequestParamsItem,
|
||||||
makeDefaultParams,
|
makeMockDefaultParams,
|
||||||
} from '@/views/api-test/components/config';
|
} from '@/views/api-test/components/config';
|
||||||
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
import { filterKeyValParams, parseRequestBodyFiles } from '@/views/api-test/components/utils';
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const isEdit = ref(props.isEditMode);
|
const isEdit = ref(props.isEditMode);
|
||||||
const mockDetail = ref<MockParams>(makeDefaultParams());
|
const mockDetail = ref<MockParams>(makeMockDefaultParams());
|
||||||
const isReadOnly = computed(() => !mockDetail.value.isNew && !isEdit.value);
|
const isReadOnly = computed(() => !mockDetail.value.isNew && !isEdit.value);
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
if (isReadOnly.value) {
|
if (isReadOnly.value) {
|
||||||
|
@ -538,13 +538,17 @@
|
||||||
appendDefaultMatchRuleItem();
|
appendDefaultMatchRuleItem();
|
||||||
}
|
}
|
||||||
isEdit.value = !!props.isEditMode;
|
isEdit.value = !!props.isEditMode;
|
||||||
|
if (props.isEditMode) {
|
||||||
|
// 从表格的编辑按钮进入,给空表格添加默认行
|
||||||
|
appendDefaultMatchRuleItem();
|
||||||
|
}
|
||||||
if (mockDetail.value.mockMatchRule.body.bodyType !== RequestBodyFormat.NONE) {
|
if (mockDetail.value.mockMatchRule.body.bodyType !== RequestBodyFormat.NONE) {
|
||||||
activeTab.value = RequestComposition.BODY;
|
activeTab.value = RequestComposition.BODY;
|
||||||
} else if (mockDetail.value.mockMatchRule.header.matchRules.length > 0) {
|
} else if (mockDetail.value.mockMatchRule.header.matchRules.length > 0) {
|
||||||
activeTab.value = RequestComposition.HEADER;
|
activeTab.value = RequestComposition.HEADER;
|
||||||
} else if (mockDetail.value.mockMatchRule.query) {
|
} else if (mockDetail.value.mockMatchRule.query.matchRules.length > 0) {
|
||||||
activeTab.value = RequestComposition.QUERY;
|
activeTab.value = RequestComposition.QUERY;
|
||||||
} else if (mockDetail.value.mockMatchRule.rest) {
|
} else if (mockDetail.value.mockMatchRule.rest.matchRules.length > 0) {
|
||||||
activeTab.value = RequestComposition.REST;
|
activeTab.value = RequestComposition.REST;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -609,7 +613,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCancel() {
|
function handleCancel() {
|
||||||
mockDetail.value = makeDefaultParams();
|
mockDetail.value = makeMockDefaultParams();
|
||||||
isEdit.value = false;
|
isEdit.value = false;
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
}
|
}
|
||||||
|
@ -624,101 +628,107 @@
|
||||||
handleCancel();
|
handleCancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleSave(isContinue = false) {
|
const mockFormRef = ref<FormInstance>();
|
||||||
try {
|
function handleSave(isContinue = false) {
|
||||||
loading.value = true;
|
mockFormRef.value?.validate(async (errors) => {
|
||||||
const { body } = mockDetail.value.mockMatchRule;
|
if (!errors) {
|
||||||
const validFormDataBodyMatchRules = filterKeyValParams(
|
try {
|
||||||
body.formDataBody.matchRules,
|
loading.value = true;
|
||||||
defaultMatchRuleItem
|
const { body } = mockDetail.value.mockMatchRule;
|
||||||
).validParams;
|
const validFormDataBodyMatchRules = filterKeyValParams(
|
||||||
const validWwwFormBodyMatchRules = filterKeyValParams(
|
body.formDataBody.matchRules,
|
||||||
body.wwwFormBody.matchRules,
|
defaultMatchRuleItem
|
||||||
defaultMatchRuleItem
|
).validParams;
|
||||||
).validParams;
|
const validWwwFormBodyMatchRules = filterKeyValParams(
|
||||||
const validHeaderMatchRules = filterKeyValParams(
|
body.wwwFormBody.matchRules,
|
||||||
mockDetail.value.mockMatchRule.header.matchRules,
|
defaultMatchRuleItem
|
||||||
defaultMatchRuleItem
|
).validParams;
|
||||||
).validParams;
|
const validHeaderMatchRules = filterKeyValParams(
|
||||||
const validQueryMatchRules = filterKeyValParams(
|
mockDetail.value.mockMatchRule.header.matchRules,
|
||||||
mockDetail.value.mockMatchRule.query.matchRules,
|
defaultMatchRuleItem
|
||||||
defaultMatchRuleItem
|
).validParams;
|
||||||
).validParams;
|
const validQueryMatchRules = filterKeyValParams(
|
||||||
const validRestMatchRules = filterKeyValParams(
|
mockDetail.value.mockMatchRule.query.matchRules,
|
||||||
mockDetail.value.mockMatchRule.rest.matchRules,
|
defaultMatchRuleItem
|
||||||
defaultMatchRuleItem
|
).validParams;
|
||||||
).validParams;
|
const validRestMatchRules = filterKeyValParams(
|
||||||
const validResponseHeaders = filterKeyValParams(
|
mockDetail.value.mockMatchRule.rest.matchRules,
|
||||||
mockDetail.value.response.headers,
|
defaultMatchRuleItem
|
||||||
defaultHeaderParamsItem
|
).validParams;
|
||||||
).validParams;
|
const validResponseHeaders = filterKeyValParams(
|
||||||
const parseFileResult = parseRequestBodyFiles(
|
mockDetail.value.response.headers,
|
||||||
mockDetail.value.mockMatchRule.body,
|
defaultHeaderParamsItem
|
||||||
[mockDetail.value.response as unknown as ResponseDefinition],
|
).validParams;
|
||||||
mockDetail.value.uploadFileIds,
|
const parseFileResult = parseRequestBodyFiles(
|
||||||
mockDetail.value.linkFileIds
|
mockDetail.value.mockMatchRule.body,
|
||||||
);
|
[mockDetail.value.response as unknown as ResponseDefinition],
|
||||||
const params: MockParams = {
|
mockDetail.value.uploadFileIds,
|
||||||
...mockDetail.value,
|
mockDetail.value.linkFileIds
|
||||||
statusCode: mockDetail.value.response.statusCode,
|
);
|
||||||
mockMatchRule: {
|
const params: MockParams = {
|
||||||
...mockDetail.value.mockMatchRule,
|
...mockDetail.value,
|
||||||
body: {
|
statusCode: mockDetail.value.response.statusCode,
|
||||||
...mockDetail.value.mockMatchRule.body,
|
mockMatchRule: {
|
||||||
formDataBody: {
|
...mockDetail.value.mockMatchRule,
|
||||||
...mockDetail.value.mockMatchRule.body.formDataBody,
|
body: {
|
||||||
matchRules: validFormDataBodyMatchRules,
|
...mockDetail.value.mockMatchRule.body,
|
||||||
|
formDataBody: {
|
||||||
|
...mockDetail.value.mockMatchRule.body.formDataBody,
|
||||||
|
matchRules: validFormDataBodyMatchRules,
|
||||||
|
},
|
||||||
|
wwwFormBody: {
|
||||||
|
...mockDetail.value.mockMatchRule.body.wwwFormBody,
|
||||||
|
matchRules: validWwwFormBodyMatchRules,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
header: {
|
||||||
|
...mockDetail.value.mockMatchRule.header,
|
||||||
|
matchRules: validHeaderMatchRules,
|
||||||
|
},
|
||||||
|
query: {
|
||||||
|
...mockDetail.value.mockMatchRule.query,
|
||||||
|
matchRules: validQueryMatchRules,
|
||||||
|
},
|
||||||
|
rest: {
|
||||||
|
...mockDetail.value.mockMatchRule.rest,
|
||||||
|
matchRules: validRestMatchRules,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
wwwFormBody: {
|
response: {
|
||||||
...mockDetail.value.mockMatchRule.body.wwwFormBody,
|
...mockDetail.value.response,
|
||||||
matchRules: validWwwFormBodyMatchRules,
|
headers: validResponseHeaders,
|
||||||
},
|
},
|
||||||
},
|
apiDefinitionId: props.definitionDetail.id,
|
||||||
header: {
|
projectId: appStore.currentProjectId,
|
||||||
...mockDetail.value.mockMatchRule.header,
|
uploadFileIds: parseFileResult.uploadFileIds,
|
||||||
matchRules: validHeaderMatchRules,
|
linkFileIds: parseFileResult.linkFileIds,
|
||||||
},
|
};
|
||||||
query: {
|
if (isEdit.value) {
|
||||||
...mockDetail.value.mockMatchRule.query,
|
await updateMock({
|
||||||
matchRules: validQueryMatchRules,
|
id: mockDetail.value.id || '',
|
||||||
},
|
...params,
|
||||||
rest: {
|
...parseFileResult,
|
||||||
...mockDetail.value.mockMatchRule.rest,
|
});
|
||||||
matchRules: validRestMatchRules,
|
Message.success(t('common.updateSuccess'));
|
||||||
},
|
} else {
|
||||||
},
|
await addMock(params);
|
||||||
response: {
|
Message.success(t('common.createSuccess'));
|
||||||
...mockDetail.value.response,
|
}
|
||||||
headers: validResponseHeaders,
|
emit('addDone');
|
||||||
},
|
if (isContinue) {
|
||||||
apiDefinitionId: props.definitionDetail.id,
|
mockDetail.value.name = '';
|
||||||
projectId: appStore.currentProjectId,
|
mockDetail.value.tags = [];
|
||||||
uploadFileIds: parseFileResult.uploadFileIds,
|
} else {
|
||||||
linkFileIds: parseFileResult.linkFileIds,
|
handleCancel();
|
||||||
};
|
}
|
||||||
if (isEdit.value) {
|
} catch (error) {
|
||||||
await updateMock({
|
// eslint-disable-next-line no-console
|
||||||
id: mockDetail.value.id || '',
|
console.log(error);
|
||||||
...params,
|
} finally {
|
||||||
...parseFileResult,
|
loading.value = false;
|
||||||
});
|
}
|
||||||
Message.success(t('common.updateSuccess'));
|
|
||||||
} else {
|
|
||||||
await addMock(params);
|
|
||||||
Message.success(t('common.createSuccess'));
|
|
||||||
}
|
}
|
||||||
emit('addDone');
|
});
|
||||||
if (isContinue) {
|
|
||||||
mockDetail.value = makeDefaultParams();
|
|
||||||
} else {
|
|
||||||
handleCancel();
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,7 @@
|
||||||
<template v-else-if="currentSelectedDefinitionResponse">
|
<template v-else-if="currentSelectedDefinitionResponse">
|
||||||
<MsTab
|
<MsTab
|
||||||
v-model:active-key="definitionActiveTab"
|
v-model:active-key="definitionActiveTab"
|
||||||
:content-tab-list="responseCompositionTabList.filter((e) => e.value !== 'DELAY')"
|
:content-tab-list="responseCompositionTabList"
|
||||||
class="no-content relative my-[8px] border-b"
|
class="no-content relative my-[8px] border-b"
|
||||||
:show-badge="false"
|
:show-badge="false"
|
||||||
/>
|
/>
|
||||||
|
@ -286,6 +286,19 @@
|
||||||
disabled
|
disabled
|
||||||
@change="() => emit('change')"
|
@change="() => emit('change')"
|
||||||
/>
|
/>
|
||||||
|
<a-input-number
|
||||||
|
v-else
|
||||||
|
v-model:model-value="mockResponse.delay"
|
||||||
|
:disabled="props.disabled"
|
||||||
|
mode="button"
|
||||||
|
:step="100"
|
||||||
|
:precision="0"
|
||||||
|
:max="600000"
|
||||||
|
:min="0"
|
||||||
|
class="w-[200px]"
|
||||||
|
>
|
||||||
|
<template #suffix> ms </template>
|
||||||
|
</a-input-number>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
|
@ -299,7 +299,7 @@
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const moduleKeyword = ref('');
|
const moduleKeyword = ref(''); // 只用于前端过滤树节点,不传入后台查询!!!
|
||||||
const folderTree = ref<ModuleTreeNode[]>([]);
|
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||||
const focusNodeKey = ref<string | number>('');
|
const focusNodeKey = ref<string | number>('');
|
||||||
const selectedKeys = ref<Array<string | number>>([props.activeModule]);
|
const selectedKeys = ref<Array<string | number>>([props.activeModule]);
|
||||||
|
@ -371,7 +371,7 @@
|
||||||
const isExpandApi = ref(false);
|
const isExpandApi = ref(false);
|
||||||
const lastModuleCountParam = ref<ApiDefinitionGetModuleParams>({
|
const lastModuleCountParam = ref<ApiDefinitionGetModuleParams>({
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
keyword: moduleKeyword.value,
|
keyword: '',
|
||||||
protocol: moduleProtocol.value,
|
protocol: moduleProtocol.value,
|
||||||
moduleIds: [],
|
moduleIds: [],
|
||||||
});
|
});
|
||||||
|
@ -427,7 +427,7 @@
|
||||||
if (props.trash) {
|
if (props.trash) {
|
||||||
res = await getTrashModuleTree({
|
res = await getTrashModuleTree({
|
||||||
// 回收站下的模块
|
// 回收站下的模块
|
||||||
keyword: moduleKeyword.value,
|
keyword: '',
|
||||||
protocol: moduleProtocol.value,
|
protocol: moduleProtocol.value,
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
moduleIds: [],
|
moduleIds: [],
|
||||||
|
@ -435,7 +435,7 @@
|
||||||
} else if (isExpandApi.value && !props.readOnly) {
|
} else if (isExpandApi.value && !props.readOnly) {
|
||||||
// 查看模块及模块下的请求
|
// 查看模块及模块下的请求
|
||||||
res = await getModuleTree({
|
res = await getModuleTree({
|
||||||
keyword: moduleKeyword.value,
|
keyword: '',
|
||||||
protocol: moduleProtocol.value,
|
protocol: moduleProtocol.value,
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
moduleIds: [],
|
moduleIds: [],
|
||||||
|
@ -443,7 +443,7 @@
|
||||||
} else {
|
} else {
|
||||||
res = await getModuleTreeOnlyModules({
|
res = await getModuleTreeOnlyModules({
|
||||||
// 只查看模块
|
// 只查看模块
|
||||||
keyword: moduleKeyword.value,
|
keyword: '',
|
||||||
protocol: moduleProtocol.value,
|
protocol: moduleProtocol.value,
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
moduleIds: [],
|
moduleIds: [],
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
:default-param-item="defaultNormalParamItem"
|
:default-param-item="defaultNormalParamItem"
|
||||||
:draggable="false"
|
:draggable="false"
|
||||||
:selectable="false"
|
:selectable="false"
|
||||||
@change="handleParamTableChange"
|
@change="handleCommonVariablesChange"
|
||||||
@batch-add="batchAddKeyValVisible = true"
|
@batch-add="() => (batchAddKeyValVisible = true)"
|
||||||
/>
|
/>
|
||||||
<paramTable
|
<paramTable
|
||||||
v-else
|
v-else
|
||||||
|
@ -37,12 +37,19 @@
|
||||||
:default-param-item="defaultCsvParamItem"
|
:default-param-item="defaultCsvParamItem"
|
||||||
:draggable="false"
|
:draggable="false"
|
||||||
:selectable="false"
|
:selectable="false"
|
||||||
@change="handleParamTableChange"
|
@change="handleCsvVariablesChange"
|
||||||
@batch-add="batchAddKeyValVisible = true"
|
@batch-add="() => (batchAddKeyValVisible = true)"
|
||||||
>
|
>
|
||||||
<template #operationPre="{ record }">
|
<template #operationPre="{ record }">
|
||||||
<a-trigger trigger="click" position="br" class="scenario-csv-trigger">
|
<a-trigger
|
||||||
<MsButton type="text" class="!mr-0">{{ t('apiScenario.params.config') }}</MsButton>
|
v-model:popup-visible="record.settingVisible"
|
||||||
|
trigger="click"
|
||||||
|
position="br"
|
||||||
|
class="scenario-csv-trigger"
|
||||||
|
>
|
||||||
|
<MsButton type="text" class="!mr-0" @click="handleRecordConfig(record)">
|
||||||
|
{{ t('apiScenario.params.config') }}
|
||||||
|
</MsButton>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="scenario-csv-trigger-content">
|
<div class="scenario-csv-trigger-content">
|
||||||
<div class="mb-[16px] flex items-center">
|
<div class="mb-[16px] flex items-center">
|
||||||
|
@ -50,32 +57,32 @@
|
||||||
<!-- <div class="text-[var(--color-text-4)]">({{ record.key }})</div> -->
|
<!-- <div class="text-[var(--color-text-4)]">({{ record.key }})</div> -->
|
||||||
</div>
|
</div>
|
||||||
<div class="scenario-csv-trigger-content-scroll">
|
<div class="scenario-csv-trigger-content-scroll">
|
||||||
<a-form ref="paramFormRef" :model="record" layout="vertical">
|
<a-form ref="paramFormRef" :model="paramForm" layout="vertical">
|
||||||
<a-form-item
|
<a-form-item
|
||||||
field="key"
|
field="name"
|
||||||
:label="t('apiScenario.params.csvName')"
|
:label="t('apiScenario.params.csvName')"
|
||||||
:rules="[{ required: true, message: t('apiScenario.params.csvNameNotNull') }]"
|
:rules="[{ required: true, message: t('apiScenario.params.csvNameNotNull') }]"
|
||||||
asterisk-position="end"
|
asterisk-position="end"
|
||||||
class="mb-[16px]"
|
class="mb-[16px]"
|
||||||
>
|
>
|
||||||
<a-input v-model:model-value="record.key" :max-length="255"></a-input>
|
<a-input v-model:model-value="paramForm.name" :max-length="255"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="variableNames" :label="t('apiScenario.params.csvParamName')" class="mb-[16px]">
|
<a-form-item field="variableNames" :label="t('apiScenario.params.csvParamName')" class="mb-[16px]">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:model-value="record.variableNames"
|
v-model:model-value="paramForm.variableNames"
|
||||||
:placeholder="t('apiScenario.params.csvParamNamePlaceholder')"
|
:placeholder="t('apiScenario.params.csvParamNamePlaceholder')"
|
||||||
></a-input>
|
></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="encoding" :label="t('apiScenario.params.csvFileCode')" class="mb-[16px]">
|
<a-form-item field="encoding" :label="t('apiScenario.params.csvFileCode')" class="mb-[16px]">
|
||||||
<a-select
|
<a-select
|
||||||
v-model:model-value="record.encoding"
|
v-model:model-value="paramForm.encoding"
|
||||||
:options="encodingOptions"
|
:options="encodingOptions"
|
||||||
class="w-[120px]"
|
class="w-[120px]"
|
||||||
></a-select>
|
></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="delimiter" :label="t('apiScenario.params.csvSplitChar')" class="mb-[16px]">
|
<a-form-item field="delimiter" :label="t('apiScenario.params.csvSplitChar')" class="mb-[16px]">
|
||||||
<a-input
|
<a-input
|
||||||
v-model:model-value="record.delimiter"
|
v-model:model-value="paramForm.delimiter"
|
||||||
:placeholder="t('common.pleaseInput')"
|
:placeholder="t('common.pleaseInput')"
|
||||||
:max-length="64"
|
:max-length="64"
|
||||||
class="w-[120px]"
|
class="w-[120px]"
|
||||||
|
@ -86,37 +93,41 @@
|
||||||
:label="t('apiScenario.params.csvIgnoreFirstLine')"
|
:label="t('apiScenario.params.csvIgnoreFirstLine')"
|
||||||
class="mb-[16px]"
|
class="mb-[16px]"
|
||||||
>
|
>
|
||||||
<a-radio-group v-model:model-value="record.ignoreFirstLine">
|
<a-radio-group v-model:model-value="paramForm.ignoreFirstLine">
|
||||||
<a-radio :value="false">False</a-radio>
|
<a-radio :value="false">False</a-radio>
|
||||||
<a-radio :value="true">True</a-radio>
|
<a-radio :value="true">True</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="random" :label="t('apiScenario.params.csvIsRandom')" class="mb-[16px]">
|
<a-form-item field="random" :label="t('apiScenario.params.csvIsRandom')" class="mb-[16px]">
|
||||||
<a-radio-group v-model:model-value="record.random">
|
<a-radio-group v-model:model-value="paramForm.random">
|
||||||
<a-radio :value="false">False</a-radio>
|
<a-radio :value="false">False</a-radio>
|
||||||
<a-radio :value="true">True</a-radio>
|
<a-radio :value="true">True</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="allowQuotedData" :label="t('apiScenario.params.csvQuoteAllow')" class="mb-[16px]">
|
<a-form-item field="allowQuotedData" :label="t('apiScenario.params.csvQuoteAllow')" class="mb-[16px]">
|
||||||
<a-radio-group v-model:model-value="record.allowQuotedData">
|
<a-radio-group v-model:model-value="paramForm.allowQuotedData">
|
||||||
<a-radio :value="false">False</a-radio>
|
<a-radio :value="false">False</a-radio>
|
||||||
<a-radio :value="true">True</a-radio>
|
<a-radio :value="true">True</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="recycleOnEof" :label="t('apiScenario.params.csvRecycle')" class="mb-[16px]">
|
<a-form-item field="recycleOnEof" :label="t('apiScenario.params.csvRecycle')" class="mb-[16px]">
|
||||||
<a-radio-group v-model:model-value="record.recycleOnEof">
|
<a-radio-group v-model:model-value="paramForm.recycleOnEof">
|
||||||
<a-radio :value="false">False</a-radio>
|
<a-radio :value="false">False</a-radio>
|
||||||
<a-radio :value="true">True</a-radio>
|
<a-radio :value="true">True</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item field="stopThreadOnEof" :label="t('apiScenario.params.csvStop')" class="mb-[16px]">
|
<a-form-item field="stopThreadOnEof" :label="t('apiScenario.params.csvStop')" class="mb-[16px]">
|
||||||
<a-radio-group v-model:model-value="record.stopThreadOnEof">
|
<a-radio-group v-model:model-value="paramForm.stopThreadOnEof">
|
||||||
<a-radio :value="false">False</a-radio>
|
<a-radio :value="false">False</a-radio>
|
||||||
<a-radio :value="true">True</a-radio>
|
<a-radio :value="true">True</a-radio>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center justify-end gap-[8px]">
|
||||||
|
<a-button type="secondary" @click="cancelConfig">{{ t('common.cancel') }}</a-button>
|
||||||
|
<a-button type="primary" @click="applyConfig">{{ t('ms.paramsInput.apply') }}</a-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-trigger>
|
</a-trigger>
|
||||||
|
@ -134,6 +145,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { FormInstance } from '@arco-design/web-vue';
|
import { FormInstance } from '@arco-design/web-vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||||
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
|
import batchAddKeyVal from '@/views/api-test/components/batchAddKeyVal.vue';
|
||||||
|
@ -215,7 +227,7 @@
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function handleParamTableChange(resultArr: any[], isInit?: boolean) {
|
function handleCommonVariablesChange(resultArr: any[], isInit?: boolean) {
|
||||||
commonVariables.value = [...resultArr];
|
commonVariables.value = [...resultArr];
|
||||||
if (!isInit) {
|
if (!isInit) {
|
||||||
emit('change');
|
emit('change');
|
||||||
|
@ -223,6 +235,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleCsvVariablesChange(resultArr: any[], isInit?: boolean) {
|
||||||
|
csvVariables.value = [...resultArr];
|
||||||
|
if (!isInit) {
|
||||||
|
emit('change');
|
||||||
|
firstSearch.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 搜索
|
// 搜索
|
||||||
function handleSearch() {
|
function handleSearch() {
|
||||||
if (firstSearch.value) {
|
if (firstSearch.value) {
|
||||||
|
@ -255,8 +275,8 @@
|
||||||
const csvColumns: ParamTableColumn[] = [
|
const csvColumns: ParamTableColumn[] = [
|
||||||
{
|
{
|
||||||
title: 'apiScenario.params.csvName',
|
title: 'apiScenario.params.csvName',
|
||||||
dataIndex: 'key',
|
dataIndex: 'name',
|
||||||
slotName: 'key',
|
slotName: 'name',
|
||||||
needValidRepeat: true,
|
needValidRepeat: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -277,11 +297,11 @@
|
||||||
titleSlotName: 'typeTitle',
|
titleSlotName: 'typeTitle',
|
||||||
typeTitleTooltip: [t('apiScenario.params.csvScopedTip1'), t('apiScenario.params.csvScopedTip2')],
|
typeTitleTooltip: [t('apiScenario.params.csvScopedTip1'), t('apiScenario.params.csvScopedTip2')],
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// title: 'apiScenario.params.file',
|
title: 'apiScenario.params.file',
|
||||||
// dataIndex: 'file',
|
dataIndex: 'file',
|
||||||
// slotName: 'file',
|
slotName: 'file',
|
||||||
// },
|
},
|
||||||
{
|
{
|
||||||
title: 'apiScenario.table.columns.status',
|
title: 'apiScenario.table.columns.status',
|
||||||
dataIndex: 'enable',
|
dataIndex: 'enable',
|
||||||
|
@ -295,7 +315,8 @@
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const configFormRef = ref<FormInstance>();
|
const paramFormRef = ref<FormInstance>();
|
||||||
|
const paramForm = ref<CsvVariable>(cloneDeep(defaultCsvParamItem));
|
||||||
const encodingOptions = [
|
const encodingOptions = [
|
||||||
{
|
{
|
||||||
label: 'UTF-8',
|
label: 'UTF-8',
|
||||||
|
@ -305,6 +326,10 @@
|
||||||
label: 'UTF-16',
|
label: 'UTF-16',
|
||||||
value: 'UTF-16',
|
value: 'UTF-16',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'GBK',
|
||||||
|
value: 'GBK',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'ISO-8859-15',
|
label: 'ISO-8859-15',
|
||||||
value: 'ISO-8859-15',
|
value: 'ISO-8859-15',
|
||||||
|
@ -314,6 +339,31 @@
|
||||||
value: 'US-ASCII',
|
value: 'US-ASCII',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function handleRecordConfig(record: CsvVariable) {
|
||||||
|
paramForm.value = cloneDeep(record);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelConfig() {
|
||||||
|
paramFormRef.value?.resetFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyConfig() {
|
||||||
|
paramFormRef.value?.validate((errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
csvVariables.value = csvVariables.value.map((e) => {
|
||||||
|
if (e.id === paramForm.value.id) {
|
||||||
|
return {
|
||||||
|
...paramForm.value,
|
||||||
|
settingVisible: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
emit('change');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|
|
@ -273,7 +273,7 @@
|
||||||
try {
|
try {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const res = await getModuleTree({
|
const res = await getModuleTree({
|
||||||
keyword: moduleKeyword.value,
|
keyword: '',
|
||||||
projectId: appStore.currentProjectId,
|
projectId: appStore.currentProjectId,
|
||||||
moduleIds: [],
|
moduleIds: [],
|
||||||
});
|
});
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
v-on="propsEvent"
|
v-on="propsEvent"
|
||||||
@batch-action="handleTableBatch"
|
@batch-action="handleTableBatch"
|
||||||
@change="changeHandler"
|
@change="changeHandler"
|
||||||
@module-change="initData()"
|
@module-change="initData"
|
||||||
@cell-click="handleCellClick"
|
@cell-click="handleCellClick"
|
||||||
>
|
>
|
||||||
<template #num="{ record }">
|
<template #num="{ record }">
|
||||||
|
@ -216,7 +216,7 @@
|
||||||
</MsTag>
|
</MsTag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-[calc(100%-32px)]">
|
<div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]">
|
||||||
<!-- 脑图开始 -->
|
<!-- 脑图开始 -->
|
||||||
<MsMinder minder-type="FeatureCase" :import-json="importJson" @node-click="handleNodeClick" />
|
<MsMinder minder-type="FeatureCase" :import-json="importJson" @node-click="handleNodeClick" />
|
||||||
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
|
<MsDrawer v-model:visible="visible" :width="480" :mask="false">
|
||||||
|
|
|
@ -103,6 +103,7 @@
|
||||||
import { getFirstRouteNameByPermission, routerNameHasPermission } from '@/utils/permission';
|
import { getFirstRouteNameByPermission, routerNameHasPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import type { LoginData } from '@/models/user';
|
import type { LoginData } from '@/models/user';
|
||||||
|
import { SettingRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
import { ValidatedError } from '@arco-design/web-vue/es/form/interface';
|
import { ValidatedError } from '@arco-design/web-vue/es/form/interface';
|
||||||
|
|
||||||
|
@ -176,8 +177,11 @@
|
||||||
const { username, password } = values;
|
const { username, password } = values;
|
||||||
loginConfig.value.username = rememberPassword ? username : '';
|
loginConfig.value.username = rememberPassword ? username : '';
|
||||||
loginConfig.value.password = rememberPassword ? password : '';
|
loginConfig.value.password = rememberPassword ? password : '';
|
||||||
if (!appStore.currentProjectId || appStore.currentProjectId === 'no_such_project') {
|
if (
|
||||||
// 没有项目权限(用户所在的当前项目被禁用&用户被移除出去该项目/白板用户没有项目)
|
(!appStore.currentProjectId || appStore.currentProjectId === 'no_such_project') &&
|
||||||
|
!(router.currentRoute.value as unknown as string).startsWith(SettingRouteEnum.SETTING)
|
||||||
|
) {
|
||||||
|
// 没有项目权限(用户所在的当前项目被禁用&用户被移除出去该项目/白板用户没有项目)且访问的页面非系统菜单模块,则重定向到无项目权限页面
|
||||||
router.push({
|
router.push({
|
||||||
name: NO_PROJECT_ROUTE_NAME,
|
name: NO_PROJECT_ROUTE_NAME,
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue