fix(全局): bug修复&devLocalEnv
This commit is contained in:
parent
8965678a2c
commit
95eb0aa49e
|
@ -1 +1,2 @@
|
|||
VITE_API_BASE_URL= 'front'
|
||||
VITE_DEV_DOMAIN='http://172.16.200.18:8081/'
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
/// <reference types="vitest" />
|
||||
import baseConfig from './vite.config.base';
|
||||
import dotenv from 'dotenv';
|
||||
import { mergeConfig } from 'vite';
|
||||
import eslint from 'vite-plugin-eslint';
|
||||
|
||||
// 注入本地/开发配置环境变量(先导入的配置优先级高)
|
||||
dotenv.config({ path: ['.env.development.local', '.env.development'] });
|
||||
|
||||
export default mergeConfig(
|
||||
{
|
||||
mode: 'development',
|
||||
|
@ -13,38 +17,38 @@ export default mergeConfig(
|
|||
},
|
||||
proxy: {
|
||||
'/ws': {
|
||||
target: 'http://172.16.200.18:8081/',
|
||||
target: process.env.VITE_DEV_DOMAIN,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/front\/ws/, ''),
|
||||
ws: true,
|
||||
},
|
||||
'/front': {
|
||||
target: 'http://172.16.200.18:8081/',
|
||||
target: process.env.VITE_DEV_DOMAIN,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/front/, ''),
|
||||
},
|
||||
'/file': {
|
||||
target: 'http://172.16.200.18:8081/',
|
||||
target: process.env.VITE_DEV_DOMAIN,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/front\/file/, ''),
|
||||
},
|
||||
'/attachment': {
|
||||
target: 'http://172.16.200.18:8081/',
|
||||
target: process.env.VITE_DEV_DOMAIN,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/front\/attachment/, ''),
|
||||
},
|
||||
'/bug/attachment': {
|
||||
target: 'http://172.16.200.18:8081/',
|
||||
target: process.env.VITE_DEV_DOMAIN,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/front\/bug\/attachment/, ''),
|
||||
},
|
||||
'/plugin/image': {
|
||||
target: 'http://172.16.200.18:8081/',
|
||||
target: process.env.VITE_DEV_DOMAIN,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/front\/plugin\/image/, ''),
|
||||
},
|
||||
'/base-display': {
|
||||
target: 'http://172.16.200.18:8081/',
|
||||
target: process.env.VITE_DEV_DOMAIN,
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/front\/base-display/, ''),
|
||||
},
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
"ahooks-vue": "^0.15.1",
|
||||
"axios": "^1.6.5",
|
||||
"dayjs": "^1.11.9",
|
||||
"dotenv": "^16.4.5",
|
||||
"echarts": "^5.4.3",
|
||||
"fastq": "^1.15.0",
|
||||
"github-markdown-css": "^5.5.1",
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import MsBaseTable from '@/components/pure/ms-table/base-table.vue';
|
||||
import { MsTableColumn } from '@/components/pure/ms-table/type';
|
||||
|
|
|
@ -69,6 +69,7 @@
|
|||
import { FormItem } from '@/components/pure/ms-form-create/types';
|
||||
import MsMinderEditor from '@/components/pure/ms-minder-editor/minderEditor.vue';
|
||||
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 attachment from './attachment.vue';
|
||||
import baseInfo from './basInfo.vue';
|
||||
|
@ -87,7 +88,7 @@
|
|||
import useFeatureCaseStore from '@/store/modules/case/featureCase';
|
||||
import useMinderStore from '@/store/modules/components/minder-editor/index';
|
||||
import { MinderCustomEvent } from '@/store/modules/components/minder-editor/types';
|
||||
import { filterTree, getGenerateId, mapTree } from '@/utils';
|
||||
import { filterTree, getGenerateId, mapTree, replaceNodeInTree } from '@/utils';
|
||||
|
||||
import {
|
||||
FeatureCaseMinderEditType,
|
||||
|
@ -158,7 +159,7 @@
|
|||
loading.value = true;
|
||||
const res = await getCaseMinderTree({
|
||||
projectId: appStore.currentProjectId,
|
||||
moduleId: props.moduleId === 'all' ? '' : props.moduleId,
|
||||
moduleId: '', // 始终加载全部,然后再进入对应的模块节点
|
||||
});
|
||||
caseTree.value = mapTree<MinderJsonNode>(res, (e) => ({
|
||||
...e,
|
||||
|
@ -195,8 +196,11 @@
|
|||
disabled: true,
|
||||
},
|
||||
};
|
||||
importJson.value.treePath = [];
|
||||
window.minder.importJson(importJson.value);
|
||||
window.minder.execCommand('camera', window.minder.getRoot(), 100);
|
||||
if (props.moduleId !== 'all') {
|
||||
// 携带具体的模块 ID 加载时,进入该模块内
|
||||
nextTick(() => {
|
||||
minderStore.dispatchEvent(MinderEventName.ENTER_NODE, undefined, undefined, undefined, [
|
||||
window.minder.getNodeById(props.moduleId),
|
||||
|
@ -485,7 +489,15 @@
|
|||
node.data.isLoaded = true;
|
||||
}
|
||||
// 加载完用例数据后,更新当前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) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
@ -503,6 +515,7 @@
|
|||
window.minder.layout();
|
||||
}
|
||||
}
|
||||
setPriorityView(true, 'P');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
const { t } = useI18n();
|
||||
const props = defineProps<{
|
||||
associationType: CaseLinkEnum;
|
||||
hasNotAssociatedIds?: string[];
|
||||
saveApi?: (params: AssociateCaseRequestType) => Promise<any>;
|
||||
testPlanId?: string;
|
||||
|
@ -73,7 +74,4 @@
|
|||
}
|
||||
innerVisible.value = false;
|
||||
}
|
||||
|
||||
// TODO 关联用例脑图待替换关联类型
|
||||
const associationType = ref<keyof typeof CaseLinkEnum>('FUNCTIONAL');
|
||||
</script>
|
||||
|
|
|
@ -224,7 +224,7 @@
|
|||
</MsMinderEditor>
|
||||
<caseAssociate
|
||||
v-model:visible="caseAssociateVisible"
|
||||
v-model:currentSelectCase="currentSelectCase"
|
||||
:association-type="currentSelectCase"
|
||||
:has-not-associated-ids="selectedAssociateCasesParams.selectIds"
|
||||
:test-plan-id="props.planId"
|
||||
@success="writeAssociateCases"
|
||||
|
@ -269,6 +269,9 @@
|
|||
const props = defineProps<{
|
||||
planId: string;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'save'): void;
|
||||
}>();
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
@ -373,6 +376,7 @@
|
|||
text: t('ms.minders.item', { count: 0 }),
|
||||
resource: [caseCountTag],
|
||||
level: 3,
|
||||
disabled: true, // 只有测试集能改文本
|
||||
isNew: true,
|
||||
};
|
||||
// 环境子节点
|
||||
|
@ -381,6 +385,7 @@
|
|||
text: t('case.execute.defaultEnv'),
|
||||
resource: [envTag],
|
||||
level: 3,
|
||||
disabled: true, // 只有测试集能改文本
|
||||
isNew: true,
|
||||
};
|
||||
// 资源池子节点
|
||||
|
@ -389,6 +394,7 @@
|
|||
resource: [resourcePoolTag],
|
||||
text: t('ms.minders.defaultResourcePool'),
|
||||
level: 3,
|
||||
disabled: true, // 只有测试集能改文本
|
||||
isNew: true,
|
||||
};
|
||||
if (node.data?.level === 1) {
|
||||
|
@ -398,6 +404,7 @@
|
|||
id: getGenerateId(),
|
||||
text: t('ms.minders.defaultTestSet'),
|
||||
level: 2,
|
||||
disabled: false, // 只有测试集能改文本
|
||||
isNew: true,
|
||||
};
|
||||
} else if (node.parent?.data) {
|
||||
|
@ -407,6 +414,7 @@
|
|||
id: getGenerateId(),
|
||||
text: t('ms.minders.defaultTestSet'),
|
||||
level: 2,
|
||||
disabled: false, // 只有测试集能改文本
|
||||
isNew: true,
|
||||
};
|
||||
}
|
||||
|
@ -545,7 +553,7 @@
|
|||
});
|
||||
}
|
||||
|
||||
const currentSelectCase = ref<keyof typeof CaseLinkEnum>('FUNCTIONAL');
|
||||
const currentSelectCase = ref<CaseLinkEnum>(CaseLinkEnum.FUNCTIONAL);
|
||||
const caseAssociateVisible = ref<boolean>(false);
|
||||
|
||||
// 批量关联用例表格参数
|
||||
|
@ -592,7 +600,7 @@
|
|||
switchingConfigFormData.value = true;
|
||||
configForm.value = cloneDeep(activePlanSet.value.data);
|
||||
extraVisible.value = true;
|
||||
currentSelectCase.value = node.data?.type || 'FUNCTIONAL';
|
||||
currentSelectCase.value = (node.data?.type as unknown as CaseLinkEnum) || CaseLinkEnum.FUNCTIONAL;
|
||||
caseAssociateVisible.value = true;
|
||||
nextTick(() => {
|
||||
switchingConfigFormData.value = false;
|
||||
|
@ -671,7 +679,7 @@
|
|||
level,
|
||||
isNew: false,
|
||||
changed: false,
|
||||
disabled: level < 2,
|
||||
disabled: level !== 2, // 只有测试集能改文本
|
||||
};
|
||||
return node;
|
||||
});
|
||||
|
@ -727,6 +735,7 @@
|
|||
handleConfigCancel();
|
||||
initMinder();
|
||||
callback();
|
||||
emit('save');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<slot name="title" v-bind="_props"></slot>
|
||||
</template>
|
||||
<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">
|
||||
<slot name="extra" v-bind="_props"></slot>
|
||||
<MsTableMoreAction
|
||||
|
@ -497,6 +497,7 @@
|
|||
@apply invisible flex w-0 items-center;
|
||||
|
||||
margin-left: -4px;
|
||||
padding-right: 8px;
|
||||
height: 32px;
|
||||
border-radius: var(--border-radius-small);
|
||||
background-color: rgb(var(--primary-1));
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
<minderHeader :icon-buttons="props.iconButtons" @save="save" />
|
||||
<Navigator />
|
||||
<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]"
|
||||
>
|
||||
<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 }}
|
||||
</a-breadcrumb-item>
|
||||
</a-breadcrumb>
|
||||
|
@ -80,6 +80,7 @@
|
|||
template: 'default',
|
||||
treePath: [],
|
||||
});
|
||||
const currentTreePath = ref<MinderJsonNodeData[]>([]);
|
||||
|
||||
async function init() {
|
||||
window.editor = new Editor(mec.value, {
|
||||
|
@ -144,6 +145,14 @@
|
|||
const menuVisible = ref(false);
|
||||
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 切换的节点
|
||||
|
@ -159,15 +168,20 @@
|
|||
'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;
|
||||
} else {
|
||||
innerImportJson.value = findNodePathByKey([importJson.value.root], node.id, 'data', 'id') as MinderJson;
|
||||
}
|
||||
window.minder.importJson(innerImportJson.value);
|
||||
const root: MinderJsonNode = window.minder.getRoot();
|
||||
window.minder.toggleSelect(root); // 先取消选中
|
||||
window.minder.select(root); // 再选中,才能触发选中变化事件
|
||||
currentTreePath.value = getCurrentTreePath();
|
||||
setTimeout(() => {
|
||||
window.minder.select(window.minder.getRoot());
|
||||
window.minder.execCommand('camera', window.minder.getRoot());
|
||||
window.minder.execCommand('camera', root);
|
||||
}, 100); // TODO:暂未知渲染时机,临时延迟解决
|
||||
}
|
||||
|
||||
|
@ -220,6 +234,13 @@
|
|||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => importJson.value.treePath,
|
||||
(arr) => {
|
||||
currentTreePath.value = arr;
|
||||
}
|
||||
);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
minderStore.setMinderUnsaved(false);
|
||||
});
|
||||
|
|
|
@ -97,7 +97,9 @@
|
|||
(val) => {
|
||||
const node: MinderJsonNode = window.minder.getSelectedNode();
|
||||
if (val && node) {
|
||||
window.minder.execCommand('camera', node, 100);
|
||||
nextTick(() => {
|
||||
window.minder.execCommand('camera', node, 100);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -36,7 +36,7 @@ export interface MinderJsonNode {
|
|||
export interface MinderJson {
|
||||
root: MinderJsonNode;
|
||||
template: string;
|
||||
treePath: MinderJsonNode[];
|
||||
treePath: MinderJsonNodeData[];
|
||||
}
|
||||
// 脑图类
|
||||
export interface MinderClass {
|
||||
|
|
|
@ -62,6 +62,13 @@
|
|||
// 临时值,用于组件内部变更,但未影响到实际值
|
||||
const tempActiveKey = ref(activeKey.value);
|
||||
|
||||
watch(
|
||||
() => activeKey.value,
|
||||
(val) => {
|
||||
tempActiveKey.value = val;
|
||||
}
|
||||
);
|
||||
|
||||
function handleTabClick(value: string) {
|
||||
if (value === activeKey.value) {
|
||||
return;
|
||||
|
|
|
@ -32,11 +32,11 @@ export default {
|
|||
'asyncTask.uploadFileSuccessTitle': 'Upload completed',
|
||||
// 通用业务提示
|
||||
'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.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':
|
||||
'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',
|
||||
};
|
||||
|
|
|
@ -31,9 +31,9 @@ export default {
|
|||
'asyncTask.uploadFileSuccess': '文件上传完成:成功 {done} 个,失败 {fail} 个',
|
||||
'asyncTask.uploadFileSuccessTitle': '上传完成',
|
||||
// 通用业务提示
|
||||
'user.openSourceCreateUsersLimit': '系统用户数已达到最大用户数限制30人(社区版),如需添加更多用户,可申请',
|
||||
'user.openSourceCreateUsersLimit': '系统用户数已达到最大用户数限制30人(社区版),如需添加/启用更多用户,可申请',
|
||||
'user.businessTry': '企业版试用',
|
||||
'user.businessCreateUsersLimitThirty': '系统用户数已达到最大用户数限制30人 (社区版),如需添加更多用户,可申请',
|
||||
'user.businessCreateUsersLimitMax': '系统用户数已达到最大用户订阅数 {count} 人,如需添加更多用户,可申请',
|
||||
'user.businessCreateUsersLimitThirty': '系统用户数已达到最大用户数限制30人 (社区版),如需添加/启用更多用户,可申请',
|
||||
'user.businessCreateUsersLimitMax': '系统用户数已达到最大用户订阅数 {count} 人,如需添加/启用更多用户,可申请',
|
||||
'user.businessScaling': '企业版扩容',
|
||||
};
|
||||
|
|
|
@ -255,7 +255,6 @@
|
|||
</template>
|
||||
<!-- 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]">
|
||||
<a-input
|
||||
v-model:model-value="condition.name"
|
||||
|
|
|
@ -1164,6 +1164,20 @@
|
|||
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(
|
||||
() => requestVModel.value.id,
|
||||
async () => {
|
||||
|
@ -1189,6 +1203,7 @@
|
|||
// 如果定义有参数BODY/QUERY/REST,用例默认tab是参数tab
|
||||
requestVModel.value.activeTab = contentTabList.value[1].value;
|
||||
}
|
||||
setDefaultActiveTab();
|
||||
if (!props.isCase) {
|
||||
responseRef.value?.setActiveResponse(requestVModel.value.mode === 'debug' ? 'result' : 'content');
|
||||
}
|
||||
|
|
|
@ -233,6 +233,7 @@
|
|||
...cloneDeep(defaultDebugParams),
|
||||
id,
|
||||
isNew: !defaultProps?.id, // 新开的tab标记为前端新增的调试,因为此时都已经有id了;但是如果是查看打开的会有携带id
|
||||
protocol: activeDebug.value.protocol, // 新开的tab默认使用当前激活的tab的协议
|
||||
...defaultProps,
|
||||
});
|
||||
activeDebug.value = debugTabs.value[debugTabs.value.length - 1];
|
||||
|
|
|
@ -202,7 +202,7 @@ export default {
|
|||
'apiTestDebug.searchByDataBaseName': 'Search by data source name',
|
||||
'apiTestDebug.regexMatchRules': 'Expression matching rules',
|
||||
'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.saveAsApi': 'Save as Api',
|
||||
'apiTestDebug.assertionItem': 'Assertion item',
|
||||
|
|
|
@ -188,7 +188,7 @@ export default {
|
|||
'apiTestDebug.testSuccess': '测试成功',
|
||||
'apiTestDebug.searchByDataBaseName': '按数据源名称搜索',
|
||||
'apiTestDebug.regexMatchRules': '表达式匹配规则',
|
||||
'apiTestDebug.extractValueTitleTip': '输入按列存储中的列名和对应的数值,如提取name列的第一个值则输入name_1',
|
||||
'apiTestDebug.extractValueTitleTip': '输入按列存储中的列名和对应的数值,如提取 id 列的第一个值则输入 id_1',
|
||||
'apiTestDebug.responseRepeatMessage': '名称重复,请重新输入',
|
||||
'apiTestDebug.saveAsApi': '另存为接口',
|
||||
'apiTestDebug.assertionItem': '断言项',
|
||||
|
|
|
@ -306,6 +306,7 @@
|
|||
id,
|
||||
isNew: !defaultProps?.id, // 新开的tab标记为前端新增的调试,因为此时都已经有id了;但是如果是查看打开的会有携带id
|
||||
definitionActiveKey: !defaultProps ? 'definition' : 'preview',
|
||||
protocol: activeApiTab.value.protocol, // 新开的tab默认使用当前激活的tab的协议
|
||||
...defaultProps,
|
||||
});
|
||||
activeApiTab.value = apiTabs.value[apiTabs.value.length - 1];
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
@close="handleClose"
|
||||
>
|
||||
<template #title>
|
||||
<div class="flex max-w-[60%] items-center gap-[8px]">
|
||||
<div class="flex flex-1 items-center gap-[8px] overflow-hidden">
|
||||
<div
|
||||
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"
|
||||
|
@ -26,30 +26,30 @@
|
|||
:step="props.step"
|
||||
/>
|
||||
<a-tooltip v-if="!isShowEditStepNameInput" :content="title" position="bottom">
|
||||
<div class="flex items-center gap-[4px]">
|
||||
<div class="one-line-text max-w-[300px]">
|
||||
<div class="flex flex-1 items-center gap-[4px] overflow-hidden">
|
||||
<div class="one-line-text">
|
||||
{{ title }}
|
||||
</div>
|
||||
<MsIcon
|
||||
v-if="!props.step || !props.step.isQuoteScenarioStep"
|
||||
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"
|
||||
/>
|
||||
</div>
|
||||
</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>
|
||||
<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-if="!props.step || props.step?.stepType === ScenarioStepType.CUSTOM_REQUEST"
|
||||
|
@ -1156,6 +1156,20 @@
|
|||
// const showAddDependencyDrawer = ref(false);
|
||||
// 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() {
|
||||
try {
|
||||
loading.value = true;
|
||||
|
@ -1287,6 +1301,7 @@
|
|||
});
|
||||
}
|
||||
requestVModel.value.activeTab = contentTabList.value[0].value;
|
||||
setDefaultActiveTab();
|
||||
nextTick(() => {
|
||||
isSwitchingContent.value = false;
|
||||
});
|
||||
|
|
|
@ -28,17 +28,15 @@
|
|||
@press-enter="updateStepName"
|
||||
@blur="updateStepName"
|
||||
/>
|
||||
<div v-show="!isShowEditStepNameInput" class="flex flex-1 items-center justify-between">
|
||||
<div class="flex items-center gap-[8px]">
|
||||
<div v-show="!isShowEditStepNameInput" class="flex flex-1 items-center justify-between overflow-hidden">
|
||||
<div class="flex flex-1 items-center gap-[8px] overflow-hidden">
|
||||
<a-tooltip :content="requestVModel.stepName || activeStep?.name">
|
||||
<div class="one-line-text max-w-[300px]">
|
||||
{{ requestVModel.stepName || characterLimit(activeStep?.name) }}</div
|
||||
>
|
||||
<div class="one-line-text"> {{ requestVModel.stepName || characterLimit(activeStep?.name) }}</div>
|
||||
</a-tooltip>
|
||||
<MsIcon
|
||||
v-if="!activeStep || !activeStep.isQuoteScenarioStep"
|
||||
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"
|
||||
/>
|
||||
</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 替换的新步骤
|
||||
|
@ -1063,6 +1075,7 @@
|
|||
await initQuoteCaseDetail();
|
||||
}
|
||||
handleActiveDebugProtocolChange(requestVModel.value.protocol);
|
||||
setDefaultActiveTab();
|
||||
nextTick(() => {
|
||||
isSwitchingContent.value = false;
|
||||
});
|
||||
|
|
|
@ -30,24 +30,25 @@
|
|||
<pre class="response-header-pre">{{ currentResponse?.console }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<responseResult
|
||||
v-else
|
||||
:active-tab="ResponseComposition.BODY"
|
||||
:request-result="currentResponse"
|
||||
:console="currentResponse?.console"
|
||||
:show-empty="false"
|
||||
:is-edit="false"
|
||||
is-definition
|
||||
>
|
||||
<template #titleLeft>
|
||||
<div class="flex items-center text-[14px]">
|
||||
<div class="font-medium text-[var(--color-text-1)]">{{ t('apiScenario.response') }}</div>
|
||||
<a-tooltip :content="props.step.name">
|
||||
<div class="one-line-text">({{ props.step.name }})</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</responseResult>
|
||||
<div v-else class="response-result">
|
||||
<responseResult
|
||||
:active-tab="ResponseComposition.BODY"
|
||||
:request-result="currentResponse"
|
||||
:console="currentResponse?.console"
|
||||
:show-empty="false"
|
||||
:is-edit="false"
|
||||
is-definition
|
||||
>
|
||||
<template #titleLeft>
|
||||
<div class="flex items-center text-[14px]">
|
||||
<div class="font-medium text-[var(--color-text-1)]">{{ t('apiScenario.response') }}</div>
|
||||
<a-tooltip :content="props.step.name">
|
||||
<div class="one-line-text">({{ props.step.name }})</div>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</responseResult>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -129,17 +130,21 @@
|
|||
padding: 8px 12px;
|
||||
border-radius: var(--border-radius-small);
|
||||
}
|
||||
.response {
|
||||
.response-head {
|
||||
background-color: var(--color-text-n9);
|
||||
}
|
||||
.response-result {
|
||||
@apply h-full overflow-auto bg-white;
|
||||
.ms-scroll-bar();
|
||||
.response {
|
||||
.response-head {
|
||||
background-color: var(--color-text-n9);
|
||||
}
|
||||
|
||||
border: 1px solid var(--color-text-n8);
|
||||
border-radius: var(--border-radius-small);
|
||||
.arco-spin {
|
||||
padding: 0;
|
||||
.response-container {
|
||||
padding: 0 16px 14px;
|
||||
border: 1px solid var(--color-text-n8);
|
||||
border-radius: var(--border-radius-small);
|
||||
.arco-spin {
|
||||
padding: 0;
|
||||
.response-container {
|
||||
padding: 0 16px 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="h-full">
|
||||
<div class="mb-[8px] flex items-center gap-[8px]">
|
||||
<a-input
|
||||
v-model:model-value="moduleKeyword"
|
||||
|
@ -51,7 +51,7 @@
|
|||
</div>
|
||||
<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
|
||||
v-model:focus-node-key="focusNodeKey"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
|
@ -178,16 +178,8 @@
|
|||
const { openModal } = useModal();
|
||||
|
||||
const virtualListProps = computed(() => {
|
||||
if (props.readOnly) {
|
||||
return {
|
||||
height: 'calc(60vh - 325px)',
|
||||
threshold: 200,
|
||||
fixedSize: true,
|
||||
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||
};
|
||||
}
|
||||
return {
|
||||
height: 'calc(100vh - 300px)',
|
||||
height: '100%',
|
||||
threshold: 200,
|
||||
fixedSize: true,
|
||||
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
</div>
|
||||
<a-tooltip v-else :content="step.name">
|
||||
<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 }}
|
||||
</div>
|
||||
<MsIcon
|
||||
|
@ -155,11 +155,7 @@
|
|||
</div>
|
||||
<a-tooltip :content="step.name" :disabled="!step.name">
|
||||
<div class="step-name-container">
|
||||
<div
|
||||
: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)]`"
|
||||
>
|
||||
<div class="step-name-text one-line-text font-normal">
|
||||
{{ step.name || t('apiScenario.pleaseInputStepDesc') }}
|
||||
</div>
|
||||
<MsIcon
|
||||
|
@ -1376,6 +1372,9 @@
|
|||
background-color: var(--color-text-n9) !important;
|
||||
.arco-tree-node-title {
|
||||
background-color: var(--color-text-n9) !important;
|
||||
.step-name-text {
|
||||
max-width: calc(100% - 244px) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
.arco-tree-node-title {
|
||||
|
@ -1391,7 +1390,7 @@
|
|||
gap: 8px;
|
||||
}
|
||||
.step-name-container {
|
||||
@apply flex items-center;
|
||||
@apply flex flex-1 items-center overflow-hidden;
|
||||
|
||||
margin-right: 16px;
|
||||
&:hover {
|
||||
|
@ -1399,6 +1398,11 @@
|
|||
@apply visible;
|
||||
}
|
||||
}
|
||||
.step-name-text {
|
||||
margin-right: 4px;
|
||||
max-width: calc(100% - 170px);
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
.edit-script-name-icon {
|
||||
@apply invisible cursor-pointer;
|
||||
|
||||
|
@ -1431,6 +1435,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
:deep(.step-tree-node-title) {
|
||||
@apply w-full;
|
||||
}
|
||||
:deep(.step-tree-node-focus) {
|
||||
background-color: var(--color-text-n9) !important;
|
||||
.arco-tree-node-title {
|
||||
|
|
|
@ -1,101 +1,97 @@
|
|||
<template>
|
||||
<MsCard no-content-padding simple>
|
||||
<div class="flex items-center justify-between p-[8px_16px_8px_16px]">
|
||||
<MsEditableTab
|
||||
v-model:active-tab="activeScenarioTab"
|
||||
v-model:tabs="scenarioTabs"
|
||||
v-permission="['PROJECT_API_SCENARIO:READ+ADD']"
|
||||
class="flex-1 overflow-hidden"
|
||||
@add="() => newTab()"
|
||||
>
|
||||
<template #label="{ tab }">
|
||||
<a-tooltip :content="tab.name || tab.label" :mouse-enter-delay="500">
|
||||
<div class="one-line-text max-w-[144px]">
|
||||
{{ tab.name || tab.label }}
|
||||
<MsSplitBox :size="300" :max="0.5">
|
||||
<template #first>
|
||||
<div class="flex h-full flex-col">
|
||||
<div class="flex-1 p-[16px]">
|
||||
<scenarioModuleTree
|
||||
ref="scenarioModuleTreeRef"
|
||||
:is-show-scenario="isShowScenario"
|
||||
@count-recycle-scenario="selectRecycleCount"
|
||||
@folder-node-select="handleNodeSelect"
|
||||
@init="handleModuleInit"
|
||||
@new-scenario="() => newTab()"
|
||||
></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>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</MsEditableTab>
|
||||
<div v-show="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]">
|
||||
<MsEnvironmentSelect :env="activeScenarioTab.environmentId" />
|
||||
<executeButton
|
||||
ref="executeButtonRef"
|
||||
v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']"
|
||||
:execute-loading="activeScenarioTab.executeLoading"
|
||||
@execute="(type) => handleExecute(type)"
|
||||
@stop-debug="handleStopExecute"
|
||||
/>
|
||||
<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>
|
||||
<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>
|
||||
</template>
|
||||
<template #second>
|
||||
<div class="flex items-center justify-between p-[8px_16px_8px_16px]">
|
||||
<MsEditableTab
|
||||
v-model:active-tab="activeScenarioTab"
|
||||
v-model:tabs="scenarioTabs"
|
||||
v-permission="['PROJECT_API_SCENARIO:READ+ADD']"
|
||||
class="flex-1 overflow-hidden"
|
||||
@add="() => newTab()"
|
||||
>
|
||||
<template #label="{ tab }">
|
||||
<a-tooltip :content="tab.name || tab.label" :mouse-enter-delay="500">
|
||||
<div class="one-line-text max-w-[144px]">
|
||||
{{ tab.name || tab.label }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #second>
|
||||
<div class="overflow-x-hidden">
|
||||
<ScenarioTable
|
||||
ref="apiTableRef"
|
||||
:active-module="activeModule"
|
||||
:offspring-ids="offspringIds"
|
||||
@refresh-module-tree="refreshTree"
|
||||
@open-scenario="openScenarioTab"
|
||||
@create-scenario="() => newTab()"
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</MsEditableTab>
|
||||
<div v-show="activeScenarioTab.id !== 'all'" class="flex items-center gap-[8px]">
|
||||
<MsEnvironmentSelect :env="activeScenarioTab.environmentId" />
|
||||
<executeButton
|
||||
ref="executeButtonRef"
|
||||
v-permission="['PROJECT_API_SCENARIO:READ+EXECUTE']"
|
||||
:execute-loading="activeScenarioTab.executeLoading"
|
||||
@execute="(type) => handleExecute(type)"
|
||||
@stop-debug="handleStopExecute"
|
||||
/>
|
||||
<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>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
</div>
|
||||
<div v-else-if="activeScenarioTab.isNew" class="pageWrap">
|
||||
<create
|
||||
ref="createRef"
|
||||
v-model:scenario="activeScenarioTab"
|
||||
:module-tree="moduleTree"
|
||||
@batch-debug="realExecute($event, false)"
|
||||
></create>
|
||||
</div>
|
||||
<div v-else class="pageWrap">
|
||||
<detail
|
||||
ref="detailRef"
|
||||
v-model:scenario="activeScenarioTab"
|
||||
:module-tree="moduleTree"
|
||||
@batch-debug="realExecute($event, false)"
|
||||
></detail>
|
||||
</div>
|
||||
</div>
|
||||
<a-divider class="!my-0" />
|
||||
<div v-if="activeScenarioTab.id === 'all'" class="pageWrap overflow-x-hidden">
|
||||
<ScenarioTable
|
||||
ref="apiTableRef"
|
||||
:active-module="activeModule"
|
||||
:offspring-ids="offspringIds"
|
||||
@refresh-module-tree="refreshTree"
|
||||
@open-scenario="openScenarioTab"
|
||||
@create-scenario="() => newTab()"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="activeScenarioTab.isNew" class="pageWrap">
|
||||
<create
|
||||
ref="createRef"
|
||||
v-model:scenario="activeScenarioTab"
|
||||
:module-tree="moduleTree"
|
||||
@batch-debug="realExecute($event, false)"
|
||||
></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>
|
||||
</template>
|
||||
|
||||
|
@ -707,43 +703,43 @@
|
|||
height: calc(100% - 50px);
|
||||
border-radius: var(--border-radius-large);
|
||||
@apply bg-white;
|
||||
.case {
|
||||
padding: 8px 4px;
|
||||
border-radius: var(--border-radius-small);
|
||||
@apply flex cursor-pointer items-center justify-between;
|
||||
&:hover {
|
||||
background-color: rgb(var(--primary-1));
|
||||
}
|
||||
.folder-icon {
|
||||
margin-right: 4px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
.folder-name {
|
||||
color: var(--color-text-1);
|
||||
}
|
||||
}
|
||||
.case {
|
||||
padding: 8px 4px;
|
||||
border-radius: var(--border-radius-small);
|
||||
@apply flex cursor-pointer items-center justify-between;
|
||||
&:hover {
|
||||
background-color: rgb(var(--primary-1));
|
||||
}
|
||||
.folder-icon {
|
||||
margin-right: 4px;
|
||||
color: var(--color-text-4);
|
||||
}
|
||||
.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 {
|
||||
margin-left: 4px;
|
||||
color: var(--color-text-4);
|
||||
color: rgb(var(--primary-5));
|
||||
}
|
||||
.case-active {
|
||||
.folder-icon,
|
||||
.folder-name,
|
||||
.folder-count {
|
||||
color: rgb(var(--primary-5));
|
||||
}
|
||||
}
|
||||
.back {
|
||||
margin-right: 8px;
|
||||
width: 20px;
|
||||
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;
|
||||
}
|
||||
.back {
|
||||
margin-right: 8px;
|
||||
width: 20px;
|
||||
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;
|
||||
}
|
||||
}
|
||||
.recycle {
|
||||
|
|
|
@ -1,48 +1,46 @@
|
|||
<template>
|
||||
<MsCard has-breadcrumb no-content-padding simple>
|
||||
<div class="p-[24px_24px_8px_24px]">
|
||||
<MsEditableTab
|
||||
v-model:active-tab="activeApiTab"
|
||||
v-model:tabs="apiTabs"
|
||||
class="flex-1 overflow-hidden"
|
||||
:show-add="false"
|
||||
:readonly="true"
|
||||
@add="newTab"
|
||||
>
|
||||
<template #label="{ tab }">
|
||||
<a-tooltip :content="tab.label" :mouse-enter-delay="500">
|
||||
<div class="one-line-text max-w-[144px]">
|
||||
{{ tab.label }}
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</MsEditableTab>
|
||||
</div>
|
||||
<a-divider class="!my-0" />
|
||||
<div v-if="activeApiTab.id === 'all'" class="pageWrap">
|
||||
<MsSplitBox :size="300" :max="0.5">
|
||||
<template #first>
|
||||
<div class="flex h-full flex-col">
|
||||
<div class="p-[16px]">
|
||||
<recycleTree
|
||||
ref="recycleTreeRef"
|
||||
:is-show-scenario="isShowScenario"
|
||||
@folder-node-select="handleNodeSelect"
|
||||
@init="handleModuleInit"
|
||||
></recycleTree>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #second>
|
||||
<MsSplitBox :size="300" :max="0.5">
|
||||
<template #first>
|
||||
<div class="h-full p-[16px]">
|
||||
<recycleTree
|
||||
ref="recycleTreeRef"
|
||||
:is-show-scenario="isShowScenario"
|
||||
@folder-node-select="handleNodeSelect"
|
||||
@init="handleModuleInit"
|
||||
></recycleTree>
|
||||
</div>
|
||||
</template>
|
||||
<template #second>
|
||||
<div class="p-[24px_24px_8px_24px]">
|
||||
<MsEditableTab
|
||||
v-model:active-tab="activeApiTab"
|
||||
v-model:tabs="apiTabs"
|
||||
class="flex-1 overflow-hidden"
|
||||
:show-add="false"
|
||||
:readonly="true"
|
||||
@add="newTab"
|
||||
>
|
||||
<template #label="{ tab }">
|
||||
<a-tooltip :content="tab.label" :mouse-enter-delay="500">
|
||||
<div class="one-line-text max-w-[144px]">
|
||||
{{ tab.label }}
|
||||
</div>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</MsEditableTab>
|
||||
</div>
|
||||
<a-divider class="!my-0" />
|
||||
<div v-if="activeApiTab.id === 'all'" class="pageWrap">
|
||||
<RecycleTable
|
||||
ref="apiTableRef"
|
||||
:active-module="activeModule"
|
||||
:offspring-ids="offspringIds"
|
||||
@refresh-module-tree="refreshTree"
|
||||
/>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</MsSplitBox>
|
||||
<!-- <detail v-else v-model:scenario="activeApiTab"></detail> -->
|
||||
</MsCard>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="h-full">
|
||||
<div class="folder" @click="setActiveFolder('all')">
|
||||
<div :class="allFolderClass">
|
||||
<MsIcon type="icon-icon_folder_filled1" class="folder-icon" />
|
||||
|
@ -22,7 +22,7 @@
|
|||
allow-clear
|
||||
/>
|
||||
</div>
|
||||
<a-spin class="w-full" :loading="loading">
|
||||
<a-spin class="w-full" :style="{ height: `calc(100vh - 248px)` }" :loading="loading">
|
||||
<MsTree
|
||||
v-model:focus-node-key="focusNodeKey"
|
||||
v-model:selected-keys="selectedKeys"
|
||||
|
@ -102,7 +102,7 @@
|
|||
|
||||
const virtualListProps = computed(() => {
|
||||
return {
|
||||
height: 'calc(100vh - 343px)',
|
||||
height: '100%',
|
||||
threshold: 200,
|
||||
fixedSize: true,
|
||||
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||
|
|
|
@ -210,7 +210,6 @@
|
|||
<div class="mt-[16px] h-[calc(100%-32px)] border-t border-[var(--color-text-n8)]">
|
||||
<!-- 脑图开始 -->
|
||||
<MsFeatureCaseMinder
|
||||
minder-type="FeatureCase"
|
||||
:module-id="props.activeFolder"
|
||||
:modules-count="props.modulesCount"
|
||||
:module-name="props.moduleName"
|
||||
|
|
|
@ -102,7 +102,7 @@
|
|||
</MsCard>
|
||||
<!-- special-height的174: 上面卡片高度158 + mt的16 -->
|
||||
<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
|
||||
v-if="activeTab === 'featureCase'"
|
||||
ref="featureCaseRef"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="flex h-full flex-col">
|
||||
<MsNotRemind tip="testPlan.planTip" class="p-[16px]" type="info" visited-key="testPlanTip" />
|
||||
<div class="flex-1 overflow-hidden p-[16px]">
|
||||
<MsTestPlanMinder :plan-id="props.planId" />
|
||||
<div class="flex h-full flex-col p-[16px]">
|
||||
<MsNotRemind tip="testPlan.planTip" class="mb-[16px]" type="info" visited-key="testPlanTip" />
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<MsTestPlanMinder :plan-id="props.planId" @save="emit('refresh')" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -14,6 +14,9 @@
|
|||
const props = defineProps<{
|
||||
planId: string;
|
||||
}>();
|
||||
const emit = defineEmits<{
|
||||
(e: 'refresh'): void;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
|
Loading…
Reference in New Issue