fix(全局): bug修复&devLocalEnv

This commit is contained in:
baiqi 2024-06-14 17:55:37 +08:00 committed by Craftsman
parent 8965678a2c
commit 95eb0aa49e
31 changed files with 375 additions and 276 deletions

View File

@ -1 +1,2 @@
VITE_API_BASE_URL= 'front' VITE_API_BASE_URL= 'front'
VITE_DEV_DOMAIN='http://172.16.200.18:8081/'

View File

@ -1,8 +1,12 @@
/// <reference types="vitest" /> /// <reference types="vitest" />
import baseConfig from './vite.config.base'; import baseConfig from './vite.config.base';
import dotenv from 'dotenv';
import { mergeConfig } from 'vite'; import { mergeConfig } from 'vite';
import eslint from 'vite-plugin-eslint'; import eslint from 'vite-plugin-eslint';
// 注入本地/开发配置环境变量(先导入的配置优先级高)
dotenv.config({ path: ['.env.development.local', '.env.development'] });
export default mergeConfig( export default mergeConfig(
{ {
mode: 'development', mode: 'development',
@ -13,38 +17,38 @@ export default mergeConfig(
}, },
proxy: { proxy: {
'/ws': { '/ws': {
target: 'http://172.16.200.18:8081/', target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/ws/, ''), rewrite: (path: string) => path.replace(/^\/front\/ws/, ''),
ws: true, ws: true,
}, },
'/front': { '/front': {
target: 'http://172.16.200.18:8081/', target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front/, ''), rewrite: (path: string) => path.replace(/^\/front/, ''),
}, },
'/file': { '/file': {
target: 'http://172.16.200.18:8081/', target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/file/, ''), rewrite: (path: string) => path.replace(/^\/front\/file/, ''),
}, },
'/attachment': { '/attachment': {
target: 'http://172.16.200.18:8081/', target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/attachment/, ''), rewrite: (path: string) => path.replace(/^\/front\/attachment/, ''),
}, },
'/bug/attachment': { '/bug/attachment': {
target: 'http://172.16.200.18:8081/', target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/bug\/attachment/, ''), rewrite: (path: string) => path.replace(/^\/front\/bug\/attachment/, ''),
}, },
'/plugin/image': { '/plugin/image': {
target: 'http://172.16.200.18:8081/', target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/plugin\/image/, ''), rewrite: (path: string) => path.replace(/^\/front\/plugin\/image/, ''),
}, },
'/base-display': { '/base-display': {
target: 'http://172.16.200.18:8081/', target: process.env.VITE_DEV_DOMAIN,
changeOrigin: true, changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/front\/base-display/, ''), rewrite: (path: string) => path.replace(/^\/front\/base-display/, ''),
}, },

View File

@ -56,6 +56,7 @@
"ahooks-vue": "^0.15.1", "ahooks-vue": "^0.15.1",
"axios": "^1.6.5", "axios": "^1.6.5",
"dayjs": "^1.11.9", "dayjs": "^1.11.9",
"dotenv": "^16.4.5",
"echarts": "^5.4.3", "echarts": "^5.4.3",
"fastq": "^1.15.0", "fastq": "^1.15.0",
"github-markdown-css": "^5.5.1", "github-markdown-css": "^5.5.1",

View File

@ -32,8 +32,6 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue';
import MsButton from '@/components/pure/ms-button/index.vue'; import MsButton from '@/components/pure/ms-button/index.vue';
import MsBaseTable from '@/components/pure/ms-table/base-table.vue'; import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
import { MsTableColumn } from '@/components/pure/ms-table/type'; import { MsTableColumn } from '@/components/pure/ms-table/type';

View File

@ -69,6 +69,7 @@
import { FormItem } from '@/components/pure/ms-form-create/types'; import { FormItem } from '@/components/pure/ms-form-create/types';
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue'; import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
import type { MinderJson, MinderJsonNode, MinderJsonNodeData } from '@/components/pure/ms-minder-editor/props'; import type { MinderJson, MinderJsonNode, MinderJsonNodeData } from '@/components/pure/ms-minder-editor/props';
import { setPriorityView } from '@/components/pure/ms-minder-editor/script/tool/utils';
import { MsFileItem } from '@/components/pure/ms-upload/types'; import { MsFileItem } from '@/components/pure/ms-upload/types';
import attachment from './attachment.vue'; import attachment from './attachment.vue';
import baseInfo from './basInfo.vue'; import baseInfo from './basInfo.vue';
@ -87,7 +88,7 @@
import useFeatureCaseStore from '@/store/modules/case/featureCase'; import useFeatureCaseStore from '@/store/modules/case/featureCase';
import useMinderStore from '@/store/modules/components/minder-editor/index'; import useMinderStore from '@/store/modules/components/minder-editor/index';
import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types'; import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
import { filterTree, getGenerateId, mapTree } from '@/utils'; import { filterTree, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
import { import {
FeatureCaseMinderEditType, FeatureCaseMinderEditType,
@ -158,7 +159,7 @@
loading.value = true; loading.value = true;
const res = await getCaseMinderTree({ const res = await getCaseMinderTree({
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
moduleId: props.moduleId === 'all' ? '' : props.moduleId, moduleId: '', //
}); });
caseTree.value = mapTree<MinderJsonNode>(res, (e) => ({ caseTree.value = mapTree<MinderJsonNode>(res, (e) => ({
...e, ...e,
@ -195,8 +196,11 @@
disabled: true, disabled: true,
}, },
}; };
importJson.value.treePath = [];
window.minder.importJson(importJson.value); window.minder.importJson(importJson.value);
window.minder.execCommand('camera', window.minder.getRoot(), 100);
if (props.moduleId !== 'all') { if (props.moduleId !== 'all') {
// ID
nextTick(() => { nextTick(() => {
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [ minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
window.minder.getNodeById(props.moduleId), window.minder.getNodeById(props.moduleId),
@ -485,7 +489,15 @@
node.data.isLoaded = true; node.data.isLoaded = true;
} }
// importJson // importJson
importJson.value = window.minder.exportJson(); const currentFullJson: MinderJson = window.minder.exportJson();
const { root } = currentFullJson;
if (root.data?.id === 'NONE') {
//
importJson.value = currentFullJson;
} else {
//
replaceNodeInTree([importJson.value.root], node.data?.id || '', root, 'data', 'id');
}
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);
@ -503,6 +515,7 @@
window.minder.layout(); window.minder.layout();
} }
} }
setPriorityView(true, 'P');
} }
/** /**

View File

@ -34,6 +34,7 @@
const { t } = useI18n(); const { t } = useI18n();
const props = defineProps<{ const props = defineProps<{
associationType: CaseLinkEnum;
hasNotAssociatedIds?: string[]; hasNotAssociatedIds?: string[];
saveApi?: (params: AssociateCaseRequestType) => Promise<any>; saveApi?: (params: AssociateCaseRequestType) => Promise<any>;
testPlanId?: string; testPlanId?: string;
@ -73,7 +74,4 @@
} }
innerVisible.value = false; innerVisible.value = false;
} }
// TODO
const associationType = ref<keyof typeof CaseLinkEnum>('FUNCTIONAL');
</script> </script>

View File

@ -224,7 +224,7 @@
</MsMinderEditor> </MsMinderEditor>
<caseAssociate <caseAssociate
v-model:visible="caseAssociateVisible" v-model:visible="caseAssociateVisible"
v-model:currentSelectCase="currentSelectCase" :association-type="currentSelectCase"
:has-not-associated-ids="selectedAssociateCasesParams.selectIds" :has-not-associated-ids="selectedAssociateCasesParams.selectIds"
:test-plan-id="props.planId" :test-plan-id="props.planId"
@success="writeAssociateCases" @success="writeAssociateCases"
@ -269,6 +269,9 @@
const props = defineProps<{ const props = defineProps<{
planId: string; planId: string;
}>(); }>();
const emit = defineEmits<{
(e: 'save'): void;
}>();
const appStore = useAppStore(); const appStore = useAppStore();
const { t } = useI18n(); const { t } = useI18n();
@ -373,6 +376,7 @@
text: t('ms.minders.item', { count: 0 }), text: t('ms.minders.item', { count: 0 }),
resource: [caseCountTag], resource: [caseCountTag],
level: 3, level: 3,
disabled: true, //
isNew: true, isNew: true,
}; };
// //
@ -381,6 +385,7 @@
text: t('case.execute.defaultEnv'), text: t('case.execute.defaultEnv'),
resource: [envTag], resource: [envTag],
level: 3, level: 3,
disabled: true, //
isNew: true, isNew: true,
}; };
// //
@ -389,6 +394,7 @@
resource: [resourcePoolTag], resource: [resourcePoolTag],
text: t('ms.minders.defaultResourcePool'), text: t('ms.minders.defaultResourcePool'),
level: 3, level: 3,
disabled: true, //
isNew: true, isNew: true,
}; };
if (node.data?.level === 1) { if (node.data?.level === 1) {
@ -398,6 +404,7 @@
id: getGenerateId(), id: getGenerateId(),
text: t('ms.minders.defaultTestSet'), text: t('ms.minders.defaultTestSet'),
level: 2, level: 2,
disabled: false, //
isNew: true, isNew: true,
}; };
} else if (node.parent?.data) { } else if (node.parent?.data) {
@ -407,6 +414,7 @@
id: getGenerateId(), id: getGenerateId(),
text: t('ms.minders.defaultTestSet'), text: t('ms.minders.defaultTestSet'),
level: 2, level: 2,
disabled: false, //
isNew: true, isNew: true,
}; };
} }
@ -545,7 +553,7 @@
}); });
} }
const currentSelectCase = ref<keyof typeof CaseLinkEnum>('FUNCTIONAL'); const currentSelectCase = ref<CaseLinkEnum>(CaseLinkEnum.FUNCTIONAL);
const caseAssociateVisible = ref<boolean>(false); const caseAssociateVisible = ref<boolean>(false);
// //
@ -592,7 +600,7 @@
switchingConfigFormData.value = true; switchingConfigFormData.value = true;
configForm.value = cloneDeep(activePlanSet.value.data); configForm.value = cloneDeep(activePlanSet.value.data);
extraVisible.value = true; extraVisible.value = true;
currentSelectCase.value = node.data?.type || 'FUNCTIONAL'; currentSelectCase.value = (node.data?.type as unknown as CaseLinkEnum) || CaseLinkEnum.FUNCTIONAL;
caseAssociateVisible.value = true; caseAssociateVisible.value = true;
nextTick(() => { nextTick(() => {
switchingConfigFormData.value = false; switchingConfigFormData.value = false;
@ -671,7 +679,7 @@
level, level,
isNew: false, isNew: false,
changed: false, changed: false,
disabled: level < 2, disabled: level !== 2, //
}; };
return node; return node;
}); });
@ -727,6 +735,7 @@
handleConfigCancel(); handleConfigCancel();
initMinder(); initMinder();
callback(); callback();
emit('save');
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(error); console.log(error);

View File

@ -49,7 +49,7 @@
<slot name="title" v-bind="_props"></slot> <slot name="title" v-bind="_props"></slot>
</template> </template>
<template v-if="$slots['extra'] || props.nodeMoreActions" #extra="_props"> <template v-if="$slots['extra'] || props.nodeMoreActions" #extra="_props">
<div class="sticky right-[8px] flex items-center justify-between"> <div class="sticky right-0 flex items-center justify-between">
<div v-if="_props.hideMoreAction !== true" class="ms-tree-node-extra"> <div v-if="_props.hideMoreAction !== true" class="ms-tree-node-extra">
<slot name="extra" v-bind="_props"></slot> <slot name="extra" v-bind="_props"></slot>
<MsTableMoreAction <MsTableMoreAction
@ -497,6 +497,7 @@
@apply invisible flex w-0 items-center; @apply invisible flex w-0 items-center;
margin-left: -4px; margin-left: -4px;
padding-right: 8px;
height: 32px; height: 32px;
border-radius: var(--border-radius-small); border-radius: var(--border-radius-small);
background-color: rgb(var(--primary-1)); background-color: rgb(var(--primary-1));

View File

@ -3,11 +3,11 @@
<minderHeader :icon-buttons="props.iconButtons" @save="save" /> <minderHeader :icon-buttons="props.iconButtons" @save="save" />
<Navigator /> <Navigator />
<div <div
v-if="innerImportJson.treePath?.length > 1" v-if="currentTreePath?.length > 0"
class="absolute left-[50%] top-[24px] z-50 translate-x-[-50%] bg-white p-[8px]" class="absolute left-[50%] top-[24px] z-50 translate-x-[-50%] bg-white p-[8px]"
> >
<a-breadcrumb> <a-breadcrumb>
<a-breadcrumb-item v-for="crumb of innerImportJson.treePath" :key="crumb.name" @click="switchNode(crumb)"> <a-breadcrumb-item v-for="crumb of currentTreePath" :key="crumb.name" @click="switchNode(crumb)">
{{ crumb.text }} {{ crumb.text }}
</a-breadcrumb-item> </a-breadcrumb-item>
</a-breadcrumb> </a-breadcrumb>
@ -80,6 +80,7 @@
template: 'default', template: 'default',
treePath: [], treePath: [],
}); });
const currentTreePath = ref<MinderJsonNodeData[]>([]);
async function init() { async function init() {
window.editor = new Editor(mec.value, { window.editor = new Editor(mec.value, {
@ -144,6 +145,14 @@
const menuVisible = ref(false); const menuVisible = ref(false);
const menuPopupOffset = ref([0, 0]); const menuPopupOffset = ref([0, 0]);
function getCurrentTreePath() {
if (innerImportJson.value.root.id === 'NONE' || innerImportJson.value.treePath?.length <= 1) {
return [];
}
const index = innerImportJson.value.treePath?.findIndex((e) => e.id === innerImportJson.value.root.data?.id);
return innerImportJson.value.treePath?.filter((e, i) => i <= index) || [];
}
/** /**
* 切换脑图展示的节点层级 * 切换脑图展示的节点层级
* @param node 切换的节点 * @param node 切换的节点
@ -159,15 +168,20 @@
'id' 'id'
); );
} }
if (node.data) { if (node.id === 'NONE') {
innerImportJson.value = importJson.value;
} else if (node.data) {
innerImportJson.value = findNodePathByKey([importJson.value.root], node.data.id, 'data', 'id') as MinderJson; innerImportJson.value = findNodePathByKey([importJson.value.root], node.data.id, 'data', 'id') as MinderJson;
} else { } else {
innerImportJson.value = findNodePathByKey([importJson.value.root], node.id, 'data', 'id') as MinderJson; innerImportJson.value = findNodePathByKey([importJson.value.root], node.id, 'data', 'id') as MinderJson;
} }
window.minder.importJson(innerImportJson.value); window.minder.importJson(innerImportJson.value);
const root: MinderJsonNode = window.minder.getRoot();
window.minder.toggleSelect(root); //
window.minder.select(root); //
currentTreePath.value = getCurrentTreePath();
setTimeout(() => { setTimeout(() => {
window.minder.select(window.minder.getRoot()); window.minder.execCommand('camera', root);
window.minder.execCommand('camera', window.minder.getRoot());
}, 100); // TODO: }, 100); // TODO:
} }
@ -220,6 +234,13 @@
} }
); );
watch(
() => importJson.value.treePath,
(arr) => {
currentTreePath.value = arr;
}
);
onBeforeUnmount(() => { onBeforeUnmount(() => {
minderStore.setMinderUnsaved(false); minderStore.setMinderUnsaved(false);
}); });

View File

@ -97,7 +97,9 @@
(val) => { (val) => {
const node: MinderJsonNode = window.minder.getSelectedNode(); const node: MinderJsonNode = window.minder.getSelectedNode();
if (val && node) { if (val && node) {
window.minder.execCommand('camera', node, 100); nextTick(() => {
window.minder.execCommand('camera', node, 100);
});
} }
} }
); );

View File

@ -36,7 +36,7 @@ export interface MinderJsonNode {
export interface MinderJson { export interface MinderJson {
root: MinderJsonNode; root: MinderJsonNode;
template: string; template: string;
treePath: MinderJsonNode[]; treePath: MinderJsonNodeData[];
} }
// 脑图类 // 脑图类
export interface MinderClass { export interface MinderClass {

View File

@ -62,6 +62,13 @@
// //
const tempActiveKey = ref(activeKey.value); const tempActiveKey = ref(activeKey.value);
watch(
() => activeKey.value,
(val) => {
tempActiveKey.value = val;
}
);
function handleTabClick(value: string) { function handleTabClick(value: string) {
if (value === activeKey.value) { if (value === activeKey.value) {
return; return;

View File

@ -32,11 +32,11 @@ export default {
'asyncTask.uploadFileSuccessTitle': 'Upload completed', 'asyncTask.uploadFileSuccessTitle': 'Upload completed',
// 通用业务提示 // 通用业务提示
'user.openSourceCreateUsersLimit': 'user.openSourceCreateUsersLimit':
'The maximum number of system users has reached 30 (Community Edition). If you need to add more users, you can apply', 'The maximum number of system users has reached 30 (Community Edition). If you need to add/enable more users, you can apply',
'user.businessTry': 'Enterprise Edition Trial', 'user.businessTry': 'Enterprise Edition Trial',
'user.businessCreateUsersLimitThirty': 'user.businessCreateUsersLimitThirty':
'The maximum number of system users has reached 30 (Community Edition). If you need to add more users, you can apply', 'The maximum number of system users has reached 30 (Community Edition). If you need to add/enable more users, you can apply',
'user.businessCreateUsersLimitMax': 'user.businessCreateUsersLimitMax':
'The number of system users has reached the maximum number of user subscriptions {count}. If you want to add more users, you can apply', 'The number of system users has reached the maximum number of user subscriptions {count}. If you want to add/enable more users, you can apply',
'user.businessScaling': 'Enterprise Edition Capacity Expansion', 'user.businessScaling': 'Enterprise Edition Capacity Expansion',
}; };

View File

@ -31,9 +31,9 @@ export default {
'asyncTask.uploadFileSuccess': '文件上传完成:成功 {done} 个,失败 {fail} 个', 'asyncTask.uploadFileSuccess': '文件上传完成:成功 {done} 个,失败 {fail} 个',
'asyncTask.uploadFileSuccessTitle': '上传完成', 'asyncTask.uploadFileSuccessTitle': '上传完成',
// 通用业务提示 // 通用业务提示
'user.openSourceCreateUsersLimit': '系统用户数已达到最大用户数限制30人(社区版),如需添加更多用户,可申请', 'user.openSourceCreateUsersLimit': '系统用户数已达到最大用户数限制30人(社区版),如需添加/启用更多用户,可申请',
'user.businessTry': '企业版试用', 'user.businessTry': '企业版试用',
'user.businessCreateUsersLimitThirty': '系统用户数已达到最大用户数限制30人 (社区版),如需添加更多用户,可申请', 'user.businessCreateUsersLimitThirty': '系统用户数已达到最大用户数限制30人 (社区版),如需添加/启用更多用户,可申请',
'user.businessCreateUsersLimitMax': '系统用户数已达到最大用户订阅数 {count} 人,如需添加更多用户,可申请', 'user.businessCreateUsersLimitMax': '系统用户数已达到最大用户订阅数 {count} 人,如需添加/启用更多用户,可申请',
'user.businessScaling': '企业版扩容', 'user.businessScaling': '企业版扩容',
}; };

View File

@ -255,7 +255,6 @@
</template> </template>
<!-- SQL操作 --> <!-- SQL操作 -->
<template v-else-if="condition.processorType === RequestConditionProcessor.SQL"> <template v-else-if="condition.processorType === RequestConditionProcessor.SQL">
<div class="mb-[8px] text-[var(--color-text-1)]">{{ t('ms.paramsInput.sqlOperationNameDesc') }}</div>
<div class="mb-[8px]"> <div class="mb-[8px]">
<a-input <a-input
v-model:model-value="condition.name" v-model:model-value="condition.name"

View File

@ -1164,6 +1164,20 @@
requestVModel.value.executeLoading = false; requestVModel.value.executeLoading = false;
} }
function setDefaultActiveTab() {
if (requestVModel.value.body.bodyType !== RequestBodyFormat.NONE) {
requestVModel.value.activeTab = RequestComposition.BODY;
} else if (requestVModel.value.query.length > 0) {
requestVModel.value.activeTab = RequestComposition.QUERY;
} else if (requestVModel.value.rest.length > 0) {
requestVModel.value.activeTab = RequestComposition.REST;
} else if (requestVModel.value.headers.length > 0) {
requestVModel.value.activeTab = RequestComposition.HEADER;
} else {
requestVModel.value.activeTab = RequestComposition.BODY;
}
}
watch( watch(
() => requestVModel.value.id, () => requestVModel.value.id,
async () => { async () => {
@ -1189,6 +1203,7 @@
// BODY/QUERY/RESTtabtab // BODY/QUERY/RESTtabtab
requestVModel.value.activeTab = contentTabList.value[1].value; requestVModel.value.activeTab = contentTabList.value[1].value;
} }
setDefaultActiveTab();
if (!props.isCase) { if (!props.isCase) {
responseRef.value?.setActiveResponse(requestVModel.value.mode === 'debug' ? 'result' : 'content'); responseRef.value?.setActiveResponse(requestVModel.value.mode === 'debug' ? 'result' : 'content');
} }

View File

@ -233,6 +233,7 @@
...cloneDeep(defaultDebugParams), ...cloneDeep(defaultDebugParams),
id, id,
isNew: !defaultProps?.id, // tabidid isNew: !defaultProps?.id, // tabidid
protocol: activeDebug.value.protocol, // tab使tab
...defaultProps, ...defaultProps,
}); });
activeDebug.value = debugTabs.value[debugTabs.value.length - 1]; activeDebug.value = debugTabs.value[debugTabs.value.length - 1];

View File

@ -202,7 +202,7 @@ export default {
'apiTestDebug.searchByDataBaseName': 'Search by data source name', 'apiTestDebug.searchByDataBaseName': 'Search by data source name',
'apiTestDebug.regexMatchRules': 'Expression matching rules', 'apiTestDebug.regexMatchRules': 'Expression matching rules',
'apiTestDebug.extractValueTitleTip': 'apiTestDebug.extractValueTitleTip':
'Enter the column name and corresponding value in column storage. If you want to extract the first value of the name column, enter name_1', 'Enter the column name and corresponding value in column storage. If you want to extract the first value of the id column, enter id_1',
'apiTestDebug.responseRepeatMessage': 'The name is duplicated, please re-enter it.', 'apiTestDebug.responseRepeatMessage': 'The name is duplicated, please re-enter it.',
'apiTestDebug.saveAsApi': 'Save as Api', 'apiTestDebug.saveAsApi': 'Save as Api',
'apiTestDebug.assertionItem': 'Assertion item', 'apiTestDebug.assertionItem': 'Assertion item',

View File

@ -188,7 +188,7 @@ export default {
'apiTestDebug.testSuccess': '测试成功', 'apiTestDebug.testSuccess': '测试成功',
'apiTestDebug.searchByDataBaseName': '按数据源名称搜索', 'apiTestDebug.searchByDataBaseName': '按数据源名称搜索',
'apiTestDebug.regexMatchRules': '表达式匹配规则', 'apiTestDebug.regexMatchRules': '表达式匹配规则',
'apiTestDebug.extractValueTitleTip': '输入按列存储中的列名和对应的数值,如提取name列的第一个值则输入name_1', 'apiTestDebug.extractValueTitleTip': '输入按列存储中的列名和对应的数值,如提取 id 列的第一个值则输入 id_1',
'apiTestDebug.responseRepeatMessage': '名称重复,请重新输入', 'apiTestDebug.responseRepeatMessage': '名称重复,请重新输入',
'apiTestDebug.saveAsApi': '另存为接口', 'apiTestDebug.saveAsApi': '另存为接口',
'apiTestDebug.assertionItem': '断言项', 'apiTestDebug.assertionItem': '断言项',

View File

@ -306,6 +306,7 @@
id, id,
isNew: !defaultProps?.id, // tabidid isNew: !defaultProps?.id, // tabidid
definitionActiveKey: !defaultProps ? 'definition' : 'preview', definitionActiveKey: !defaultProps ? 'definition' : 'preview',
protocol: activeApiTab.value.protocol, // tab使tab
...defaultProps, ...defaultProps,
}); });
activeApiTab.value = apiTabs.value[apiTabs.value.length - 1]; activeApiTab.value = apiTabs.value[apiTabs.value.length - 1];

View File

@ -14,7 +14,7 @@
@close="handleClose" @close="handleClose"
> >
<template #title> <template #title>
<div class="flex max-w-[60%] items-center gap-[8px]"> <div class="flex flex-1 items-center gap-[8px] overflow-hidden">
<div <div
v-if="props.step" v-if="props.step"
class="flex h-[16px] min-w-[16px] items-center justify-center rounded-full bg-[var(--color-text-brand)] pr-[2px] !text-white" class="flex h-[16px] min-w-[16px] items-center justify-center rounded-full bg-[var(--color-text-brand)] pr-[2px] !text-white"
@ -26,30 +26,30 @@
:step="props.step" :step="props.step"
/> />
<a-tooltip v-if="!isShowEditStepNameInput" :content="title" position="bottom"> <a-tooltip v-if="!isShowEditStepNameInput" :content="title" position="bottom">
<div class="flex items-center gap-[4px]"> <div class="flex flex-1 items-center gap-[4px] overflow-hidden">
<div class="one-line-text max-w-[300px]"> <div class="one-line-text">
{{ title }} {{ title }}
</div> </div>
<MsIcon <MsIcon
v-if="!props.step || !props.step.isQuoteScenarioStep" v-if="!props.step || !props.step.isQuoteScenarioStep"
type="icon-icon_edit_outlined" type="icon-icon_edit_outlined"
class="cursor-pointer hover:text-[rgb(var(--primary-5))]" class="min-w-[16px] cursor-pointer hover:text-[rgb(var(--primary-5))]"
@click="isShowEditStepNameInput = true" @click="isShowEditStepNameInput = true"
/> />
</div> </div>
</a-tooltip> </a-tooltip>
<a-input
v-if="isShowEditStepNameInput"
ref="stepNameInputRef"
v-model:model-value="requestVModel.stepName"
class="flex-1"
:placeholder="t('apiScenario.pleaseInputStepName')"
:max-length="255"
show-word-limit
@press-enter="updateStepName"
@blur="updateStepName"
/>
</div> </div>
<a-input
v-if="isShowEditStepNameInput"
ref="stepNameInputRef"
v-model:model-value="requestVModel.stepName"
class="flex-1"
:placeholder="t('apiScenario.pleaseInputStepName')"
:max-length="255"
show-word-limit
@press-enter="updateStepName"
@blur="updateStepName"
/>
<div v-show="!isShowEditStepNameInput" class="ml-auto flex items-center gap-[16px]"> <div v-show="!isShowEditStepNameInput" class="ml-auto flex items-center gap-[16px]">
<div <div
v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST" v-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
@ -1156,6 +1156,20 @@
// const showAddDependencyDrawer = ref(false); // const showAddDependencyDrawer = ref(false);
// const addDependencyMode = ref<'pre' | 'post'>('pre'); // const addDependencyMode = ref<'pre' | 'post'>('pre');
function setDefaultActiveTab() {
if (requestVModel.value.body.bodyType !== RequestBodyFormat.NONE) {
requestVModel.value.activeTab = RequestComposition.BODY;
} else if (requestVModel.value.query.length > 0) {
requestVModel.value.activeTab = RequestComposition.QUERY;
} else if (requestVModel.value.rest.length > 0) {
requestVModel.value.activeTab = RequestComposition.REST;
} else if (requestVModel.value.headers.length > 0) {
requestVModel.value.activeTab = RequestComposition.HEADER;
} else {
requestVModel.value.activeTab = RequestComposition.BODY;
}
}
async function initQuoteApiDetail() { async function initQuoteApiDetail() {
try { try {
loading.value = true; loading.value = true;
@ -1287,6 +1301,7 @@
}); });
} }
requestVModel.value.activeTab = contentTabList.value[0].value; requestVModel.value.activeTab = contentTabList.value[0].value;
setDefaultActiveTab();
nextTick(() => { nextTick(() => {
isSwitchingContent.value = false; isSwitchingContent.value = false;
}); });

View File

@ -28,17 +28,15 @@
@press-enter="updateStepName" @press-enter="updateStepName"
@blur="updateStepName" @blur="updateStepName"
/> />
<div v-show="!isShowEditStepNameInput" class="flex flex-1 items-center justify-between"> <div v-show="!isShowEditStepNameInput" class="flex flex-1 items-center justify-between overflow-hidden">
<div class="flex items-center gap-[8px]"> <div class="flex flex-1 items-center gap-[8px] overflow-hidden">
<a-tooltip :content="requestVModel.stepName || activeStep?.name"> <a-tooltip :content="requestVModel.stepName || activeStep?.name">
<div class="one-line-text max-w-[300px]"> <div class="one-line-text"> {{ requestVModel.stepName || characterLimit(activeStep?.name) }}</div>
{{ requestVModel.stepName || characterLimit(activeStep?.name) }}</div
>
</a-tooltip> </a-tooltip>
<MsIcon <MsIcon
v-if="!activeStep || !activeStep.isQuoteScenarioStep" v-if="!activeStep || !activeStep.isQuoteScenarioStep"
type="icon-icon_edit_outlined" type="icon-icon_edit_outlined"
class="cursor-pointer hover:text-[rgb(var(--primary-5))]" class="min-w-[16px] cursor-pointer hover:text-[rgb(var(--primary-5))]"
@click="showEditScriptNameInput" @click="showEditScriptNameInput"
/> />
</div> </div>
@ -1020,6 +1018,20 @@
} }
} }
function setDefaultActiveTab() {
if (requestVModel.value.body.bodyType !== RequestBodyFormat.NONE) {
requestVModel.value.activeTab = RequestComposition.BODY;
} else if (requestVModel.value.query.length > 0) {
requestVModel.value.activeTab = RequestComposition.QUERY;
} else if (requestVModel.value.rest.length > 0) {
requestVModel.value.activeTab = RequestComposition.REST;
} else if (requestVModel.value.headers.length > 0) {
requestVModel.value.activeTab = RequestComposition.HEADER;
} else {
requestVModel.value.activeTab = RequestComposition.BODY;
}
}
/** /**
* 替换步骤 * 替换步骤
* @param newStep 替换的新步骤 * @param newStep 替换的新步骤
@ -1063,6 +1075,7 @@
await initQuoteCaseDetail(); await initQuoteCaseDetail();
} }
handleActiveDebugProtocolChange(requestVModel.value.protocol); handleActiveDebugProtocolChange(requestVModel.value.protocol);
setDefaultActiveTab();
nextTick(() => { nextTick(() => {
isSwitchingContent.value = false; isSwitchingContent.value = false;
}); });

View File

@ -30,24 +30,25 @@
<pre class="response-header-pre">{{ currentResponse?.console }}</pre> <pre class="response-header-pre">{{ currentResponse?.console }}</pre>
</div> </div>
</div> </div>
<responseResult <div v-else class="response-result">
v-else <responseResult
:active-tab="ResponseComposition.BODY" :active-tab="ResponseComposition.BODY"
:request-result="currentResponse" :request-result="currentResponse"
:console="currentResponse?.console" :console="currentResponse?.console"
:show-empty="false" :show-empty="false"
:is-edit="false" :is-edit="false"
is-definition is-definition
> >
<template #titleLeft> <template #titleLeft>
<div class="flex items-center text-[14px]"> <div class="flex items-center text-[14px]">
<div class="font-medium text-[var(--color-text-1)]">{{ t('apiScenario.response') }}</div> <div class="font-medium text-[var(--color-text-1)]">{{ t('apiScenario.response') }}</div>
<a-tooltip :content="props.step.name"> <a-tooltip :content="props.step.name">
<div class="one-line-text">({{ props.step.name }})</div> <div class="one-line-text">({{ props.step.name }})</div>
</a-tooltip> </a-tooltip>
</div> </div>
</template> </template>
</responseResult> </responseResult>
</div>
</div> </div>
</div> </div>
</template> </template>
@ -129,17 +130,21 @@
padding: 8px 12px; padding: 8px 12px;
border-radius: var(--border-radius-small); border-radius: var(--border-radius-small);
} }
.response { .response-result {
.response-head { @apply h-full overflow-auto bg-white;
background-color: var(--color-text-n9); .ms-scroll-bar();
} .response {
.response-head {
background-color: var(--color-text-n9);
}
border: 1px solid var(--color-text-n8); border: 1px solid var(--color-text-n8);
border-radius: var(--border-radius-small); border-radius: var(--border-radius-small);
.arco-spin { .arco-spin {
padding: 0; padding: 0;
.response-container { .response-container {
padding: 0 16px 14px; padding: 0 16px 14px;
}
} }
} }
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="h-full">
<div class="mb-[8px] flex items-center gap-[8px]"> <div class="mb-[8px] flex items-center gap-[8px]">
<a-input <a-input
v-model:model-value="moduleKeyword" v-model:model-value="moduleKeyword"
@ -51,7 +51,7 @@
</div> </div>
<a-divider class="my-[8px]" /> <a-divider class="my-[8px]" />
<a-spin class="w-full" :style="{ height: `calc(100vh - 300px)` }" :loading="loading"> <a-spin class="w-full" :style="{ height: `calc(100vh - 248px)` }" :loading="loading">
<MsTree <MsTree
v-model:focus-node-key="focusNodeKey" v-model:focus-node-key="focusNodeKey"
v-model:selected-keys="selectedKeys" v-model:selected-keys="selectedKeys"
@ -178,16 +178,8 @@
const { openModal } = useModal(); const { openModal } = useModal();
const virtualListProps = computed(() => { const virtualListProps = computed(() => {
if (props.readOnly) {
return {
height: 'calc(60vh - 325px)',
threshold: 200,
fixedSize: true,
buffer: 15, // 10 padding
};
}
return { return {
height: 'calc(100vh - 300px)', height: '100%',
threshold: 200, threshold: 200,
fixedSize: true, fixedSize: true,
buffer: 15, // 10 padding buffer: 15, // 10 padding

View File

@ -120,7 +120,7 @@
</div> </div>
<a-tooltip v-else :content="step.name"> <a-tooltip v-else :content="step.name">
<div class="step-name-container"> <div class="step-name-container">
<div class="one-line-text mr-[4px] max-w-[350px] font-medium text-[var(--color-text-1)]"> <div class="step-name-text one-line-text font-medium">
{{ step.name }} {{ step.name }}
</div> </div>
<MsIcon <MsIcon
@ -155,11 +155,7 @@
</div> </div>
<a-tooltip :content="step.name" :disabled="!step.name"> <a-tooltip :content="step.name" :disabled="!step.name">
<div class="step-name-container"> <div class="step-name-container">
<div <div class="step-name-text one-line-text font-normal">
:class="`one-line-text mr-[4px] ${
step.stepType === ScenarioStepType.ONCE_ONLY_CONTROLLER ? 'max-w-[750px]' : 'max-w-[150px]'
} font-normal text-[var(--color-text-1)]`"
>
{{ step.name || t('apiScenario.pleaseInputStepDesc') }} {{ step.name || t('apiScenario.pleaseInputStepDesc') }}
</div> </div>
<MsIcon <MsIcon
@ -1376,6 +1372,9 @@
background-color: var(--color-text-n9) !important; background-color: var(--color-text-n9) !important;
.arco-tree-node-title { .arco-tree-node-title {
background-color: var(--color-text-n9) !important; background-color: var(--color-text-n9) !important;
.step-name-text {
max-width: calc(100% - 244px) !important;
}
} }
} }
.arco-tree-node-title { .arco-tree-node-title {
@ -1391,7 +1390,7 @@
gap: 8px; gap: 8px;
} }
.step-name-container { .step-name-container {
@apply flex items-center; @apply flex flex-1 items-center overflow-hidden;
margin-right: 16px; margin-right: 16px;
&:hover { &:hover {
@ -1399,6 +1398,11 @@
@apply visible; @apply visible;
} }
} }
.step-name-text {
margin-right: 4px;
max-width: calc(100% - 170px);
color: var(--color-text-1);
}
.edit-script-name-icon { .edit-script-name-icon {
@apply invisible cursor-pointer; @apply invisible cursor-pointer;
@ -1431,6 +1435,9 @@
} }
} }
} }
:deep(.step-tree-node-title) {
@apply w-full;
}
:deep(.step-tree-node-focus) { :deep(.step-tree-node-focus) {
background-color: var(--color-text-n9) !important; background-color: var(--color-text-n9) !important;
.arco-tree-node-title { .arco-tree-node-title {

View File

@ -1,101 +1,97 @@
<template> <template>
<MsCard no-content-padding simple> <MsCard no-content-padding simple>
<div class="flex items-center justify-between p-[8px_16px_8px_16px]"> <MsSplitBox :size="300" :max="0.5">
<MsEditableTab <template #first>
v-model:active-tab="activeScenarioTab" <div class="flex h-full flex-col">
v-model:tabs="scenarioTabs" <div class="flex-1 p-[16px]">
v-permission="['PROJECT_API_SCENARIO:READ+ADD']" <scenarioModuleTree
class="flex-1 overflow-hidden" ref="scenarioModuleTreeRef"
@add="() => newTab()" :is-show-scenario="isShowScenario"
> @count-recycle-scenario="selectRecycleCount"
<template #label="{ tab }"> @folder-node-select="handleNodeSelect"
<a-tooltip :content="tab.name || tab.label" :mouse-enter-delay="500"> @init="handleModuleInit"
<div class="one-line-text max-w-[144px]"> @new-scenario="() => newTab()"
{{ tab.name || tab.label }} ></scenarioModuleTree>
</div>
<a-divider margin="0" />
<div class="case">
<div class="flex items-center px-[20px]" :class="getActiveClass('recycle')" @click="redirectRecycle()">
<MsIcon type="icon-icon_delete-trash_outlined" class="folder-icon" />
<div class="folder-name mx-[4px]">{{ t('apiScenario.tree.recycleBin') }}</div>
<div class="folder-count">({{ recycleModulesCount || 0 }})</div>
</div> </div>
</a-tooltip> </div>
</template> </div>
</MsEditableTab> </template>
<div v-show="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]"> <template #second>
<MsEnvironmentSelect :env="activeScenarioTab.environmentId" /> <div class="flex items-center justify-between p-[8px_16px_8px_16px]">
<executeButton <MsEditableTab
ref="executeButtonRef" v-model:active-tab="activeScenarioTab"
v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']" v-model:tabs="scenarioTabs"
:execute-loading="activeScenarioTab.executeLoading" v-permission="['PROJECT_API_SCENARIO:READ+ADD']"
@execute="(type) => handleExecute(type)" class="flex-1 overflow-hidden"
@stop-debug="handleStopExecute" @add="() => newTab()"
/> >
<a-button <template #label="{ tab }">
v-if=" <a-tooltip :content="tab.name || tab.label" :mouse-enter-delay="500">
activeScenarioTab.isNew <div class="one-line-text max-w-[144px]">
? hasAnyPermission(['PROJECT_API_SCENARIO:READ+ADD']) {{ tab.name || tab.label }}
: hasAnyPermission(['PROJECT_API_SCENARIO:READ+UPDATE'])
"
type="primary"
:loading="saveLoading"
@click="saveScenario"
>
{{ t('common.save') }}
</a-button>
</div>
</div>
<a-divider class="!my-0" />
<div v-if="activeScenarioTab.id === 'all'" class="pageWrap">
<MsSplitBox :size="300" :max="0.5">
<template #first>
<div class="flex h-full flex-col">
<div class="p-[16px]">
<scenarioModuleTree
ref="scenarioModuleTreeRef"
:is-show-scenario="isShowScenario"
@count-recycle-scenario="selectRecycleCount"
@folder-node-select="handleNodeSelect"
@init="handleModuleInit"
@new-scenario="() => newTab()"
></scenarioModuleTree>
</div>
<div class="flex-1">
<a-divider margin="0" />
<div class="case">
<div class="flex items-center px-[20px]" :class="getActiveClass('recycle')" @click="redirectRecycle()">
<MsIcon type="icon-icon_delete-trash_outlined" class="folder-icon" />
<div class="folder-name mx-[4px]">{{ t('apiScenario.tree.recycleBin') }}</div>
<div class="folder-count">({{ recycleModulesCount || 0 }})</div>
</div> </div>
</div> </a-tooltip>
</div> </template>
</div> </MsEditableTab>
</template> <div v-show="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]">
<template #second> <MsEnvironmentSelect :env="activeScenarioTab.environmentId" />
<div class="overflow-x-hidden"> <executeButton
<ScenarioTable ref="executeButtonRef"
ref="apiTableRef" v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']"
:active-module="activeModule" :execute-loading="activeScenarioTab.executeLoading"
:offspring-ids="offspringIds" @execute="(type) => handleExecute(type)"
@refresh-module-tree="refreshTree" @stop-debug="handleStopExecute"
@open-scenario="openScenarioTab"
@create-scenario="() => newTab()"
/> />
<a-button
v-if="
activeScenarioTab.isNew
? hasAnyPermission(['PROJECT_API_SCENARIO:READ+ADD'])
: hasAnyPermission(['PROJECT_API_SCENARIO:READ+UPDATE'])
"
type="primary"
:loading="saveLoading"
@click="saveScenario"
>
{{ t('common.save') }}
</a-button>
</div> </div>
</template> </div>
</MsSplitBox> <a-divider class="!my-0" />
</div> <div v-if="activeScenarioTab.id === 'all'" class="pageWrap overflow-x-hidden">
<div v-else-if="activeScenarioTab.isNew" class="pageWrap"> <ScenarioTable
<create ref="apiTableRef"
ref="createRef" :active-module="activeModule"
v-model:scenario="activeScenarioTab" :offspring-ids="offspringIds"
:module-tree="moduleTree" @refresh-module-tree="refreshTree"
@batch-debug="realExecute($event, false)" @open-scenario="openScenarioTab"
></create> @create-scenario="() => newTab()"
</div> />
<div v-else class="pageWrap"> </div>
<detail <div v-else-if="activeScenarioTab.isNew" class="pageWrap">
ref="detailRef" <create
v-model:scenario="activeScenarioTab" ref="createRef"
:module-tree="moduleTree" v-model:scenario="activeScenarioTab"
@batch-debug="realExecute($event, false)" :module-tree="moduleTree"
></detail> @batch-debug="realExecute($event, false)"
</div> ></create>
</div>
<div v-else class="pageWrap">
<detail
ref="detailRef"
v-model:scenario="activeScenarioTab"
:module-tree="moduleTree"
@batch-debug="realExecute($event, false)"
></detail>
</div>
</template>
</MsSplitBox>
</MsCard> </MsCard>
</template> </template>
@ -707,43 +703,43 @@
height: calc(100% - 50px); height: calc(100% - 50px);
border-radius: var(--border-radius-large); border-radius: var(--border-radius-large);
@apply bg-white; @apply bg-white;
.case { }
padding: 8px 4px; .case {
border-radius: var(--border-radius-small); padding: 8px 4px;
@apply flex cursor-pointer items-center justify-between; border-radius: var(--border-radius-small);
&:hover { @apply flex cursor-pointer items-center justify-between;
background-color: rgb(var(--primary-1)); &:hover {
} background-color: rgb(var(--primary-1));
.folder-icon { }
margin-right: 4px; .folder-icon {
color: var(--color-text-4); margin-right: 4px;
} color: var(--color-text-4);
.folder-name { }
color: var(--color-text-1); .folder-name {
} color: var(--color-text-1);
}
.folder-count {
margin-left: 4px;
color: var(--color-text-4);
}
.case-active {
.folder-icon,
.folder-name,
.folder-count { .folder-count {
margin-left: 4px; color: rgb(var(--primary-5));
color: var(--color-text-4);
} }
.case-active { }
.folder-icon, .back {
.folder-name, margin-right: 8px;
.folder-count { width: 20px;
color: rgb(var(--primary-5)); height: 20px;
} border: 1px solid #ffffff;
} background: linear-gradient(90deg, rgb(var(--primary-9)) 3.36%, #ffffff 100%);
.back { box-shadow: 0 0 7px rgb(15 0 78 / 9%);
margin-right: 8px; .arco-icon {
width: 20px; color: rgb(var(--primary-5));
height: 20px;
border: 1px solid #ffffff;
background: linear-gradient(90deg, rgb(var(--primary-9)) 3.36%, #ffffff 100%);
box-shadow: 0 0 7px rgb(15 0 78 / 9%);
.arco-icon {
color: rgb(var(--primary-5));
}
@apply flex cursor-pointer items-center rounded-full;
} }
@apply flex cursor-pointer items-center rounded-full;
} }
} }
.recycle { .recycle {

View File

@ -1,48 +1,46 @@
<template> <template>
<MsCard has-breadcrumb no-content-padding simple> <MsCard has-breadcrumb no-content-padding simple>
<div class="p-[24px_24px_8px_24px]"> <MsSplitBox :size="300" :max="0.5">
<MsEditableTab <template #first>
v-model:active-tab="activeApiTab" <div class="h-full p-[16px]">
v-model:tabs="apiTabs" <recycleTree
class="flex-1 overflow-hidden" ref="recycleTreeRef"
:show-add="false" :is-show-scenario="isShowScenario"
:readonly="true" @folder-node-select="handleNodeSelect"
@add="newTab" @init="handleModuleInit"
> ></recycleTree>
<template #label="{ tab }"> </div>
<a-tooltip :content="tab.label" :mouse-enter-delay="500"> </template>
<div class="one-line-text max-w-[144px]"> <template #second>
{{ tab.label }} <div class="p-[24px_24px_8px_24px]">
</div> <MsEditableTab
</a-tooltip> v-model:active-tab="activeApiTab"
</template> v-model:tabs="apiTabs"
</MsEditableTab> class="flex-1 overflow-hidden"
</div> :show-add="false"
<a-divider class="!my-0" /> :readonly="true"
<div v-if="activeApiTab.id === 'all'" class="pageWrap"> @add="newTab"
<MsSplitBox :size="300" :max="0.5"> >
<template #first> <template #label="{ tab }">
<div class="flex h-full flex-col"> <a-tooltip :content="tab.label" :mouse-enter-delay="500">
<div class="p-[16px]"> <div class="one-line-text max-w-[144px]">
<recycleTree {{ tab.label }}
ref="recycleTreeRef" </div>
:is-show-scenario="isShowScenario" </a-tooltip>
@folder-node-select="handleNodeSelect" </template>
@init="handleModuleInit" </MsEditableTab>
></recycleTree> </div>
</div> <a-divider class="!my-0" />
</div> <div v-if="activeApiTab.id === 'all'" class="pageWrap">
</template>
<template #second>
<RecycleTable <RecycleTable
ref="apiTableRef" ref="apiTableRef"
:active-module="activeModule" :active-module="activeModule"
:offspring-ids="offspringIds" :offspring-ids="offspringIds"
@refresh-module-tree="refreshTree" @refresh-module-tree="refreshTree"
/> />
</template> </div>
</MsSplitBox> </template>
</div> </MsSplitBox>
<!-- <detail v-else v-model:scenario="activeApiTab"></detail> --> <!-- <detail v-else v-model:scenario="activeApiTab"></detail> -->
</MsCard> </MsCard>
</template> </template>

View File

@ -1,5 +1,5 @@
<template> <template>
<div> <div class="h-full">
<div class="folder" @click="setActiveFolder('all')"> <div class="folder" @click="setActiveFolder('all')">
<div :class="allFolderClass"> <div :class="allFolderClass">
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" /> <MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
@ -22,7 +22,7 @@
allow-clear allow-clear
/> />
</div> </div>
<a-spin class="w-full" :loading="loading"> <a-spin class="w-full" :style="{ height: `calc(100vh - 248px)` }" :loading="loading">
<MsTree <MsTree
v-model:focus-node-key="focusNodeKey" v-model:focus-node-key="focusNodeKey"
v-model:selected-keys="selectedKeys" v-model:selected-keys="selectedKeys"
@ -102,7 +102,7 @@
const virtualListProps = computed(() => { const virtualListProps = computed(() => {
return { return {
height: 'calc(100vh - 343px)', height: '100%',
threshold: 200, threshold: 200,
fixedSize: true, fixedSize: true,
buffer: 15, // 10 padding buffer: 15, // 10 padding

View File

@ -210,7 +210,6 @@
<div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]"> <div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]">
<!-- 脑图开始 --> <!-- 脑图开始 -->
<MsFeatureCaseMinder <MsFeatureCaseMinder
minder-type="FeatureCase"
:module-id="props.activeFolder" :module-id="props.activeFolder"
:modules-count="props.modulesCount" :modules-count="props.modulesCount"
:module-name="props.moduleName" :module-name="props.moduleName"

View File

@ -102,7 +102,7 @@
</MsCard> </MsCard>
<!-- special-height的174: 上面卡片高度158 + mt的16 --> <!-- special-height的174: 上面卡片高度158 + mt的16 -->
<MsCard class="mt-[16px]" :special-height="174" simple has-breadcrumb no-content-padding> <MsCard class="mt-[16px]" :special-height="174" simple has-breadcrumb no-content-padding>
<Plan v-if="activeTab === 'plan'" :plan-id="planId" /> <Plan v-if="activeTab === 'plan'" :plan-id="planId" @refresh="initDetail" />
<FeatureCase <FeatureCase
v-if="activeTab === 'featureCase'" v-if="activeTab === 'featureCase'"
ref="featureCaseRef" ref="featureCaseRef"

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="flex h-full flex-col"> <div class="flex h-full flex-col p-[16px]">
<MsNotRemind tip="testPlan.planTip" class="p-[16px]" type="info" visited-key="testPlanTip" /> <MsNotRemind tip="testPlan.planTip" class="mb-[16px]" type="info" visited-key="testPlanTip" />
<div class="flex-1 overflow-hidden p-[16px]"> <div class="flex-1 overflow-hidden">
<MsTestPlanMinder :plan-id="props.planId" /> <MsTestPlanMinder :plan-id="props.planId" @save="emit('refresh')" />
</div> </div>
</div> </div>
</template> </template>
@ -14,6 +14,9 @@
const props = defineProps<{ const props = defineProps<{
planId: string; planId: string;
}>(); }>();
const emit = defineEmits<{
(e: 'refresh'): void;
}>();
</script> </script>
<style lang="less" scoped></style> <style lang="less" scoped></style>