feat(接口测试): 接口测试接口文档导出联调&分享权限补充
This commit is contained in:
parent
60c9086f3b
commit
9ac9ff57cd
|
@ -45,6 +45,7 @@ import {
|
|||
diffDataUrl,
|
||||
ExecuteCaseUrl,
|
||||
ExportDefinitionUrl,
|
||||
ExportShareDefinitionUrl,
|
||||
GetApiDownloadFileUrl,
|
||||
GetCaseBatchExportParamsUrl,
|
||||
GetCaseDetailUrl,
|
||||
|
@ -62,6 +63,7 @@ import {
|
|||
GetModuleTreeUrl,
|
||||
GetPoolId,
|
||||
GetPoolOptionUrl,
|
||||
GetShareApiDownloadFileUrl,
|
||||
GetSharePageUrl,
|
||||
getSyncedCaseDetailUrl,
|
||||
GetTrashModuleCountUrl,
|
||||
|
@ -84,6 +86,7 @@ import {
|
|||
SortCaseUrl,
|
||||
SortDefinitionUrl,
|
||||
StopApiExportUrl,
|
||||
StopShareApiExportUrl,
|
||||
SwitchDefinitionScheduleUrl,
|
||||
ToggleFollowCaseUrl,
|
||||
ToggleFollowDefinitionUrl,
|
||||
|
@ -684,3 +687,23 @@ export function getShareModuleTree(data: ApiDefinitionGetModuleParams) {
|
|||
export function getShareModuleCount(data: ApiDefinitionGetModuleParams) {
|
||||
return MSR.post({ url: shareModuleCountUrl, data });
|
||||
}
|
||||
|
||||
// 导出分享定义文档
|
||||
export function exportShareApiDefinition(data: ApiDefinitionBatchExportParams, type: string) {
|
||||
return MSR.post({ url: `${ExportShareDefinitionUrl}/${type}`, data });
|
||||
}
|
||||
|
||||
// 获取分享导出的文件
|
||||
export function getShareApiDownloadFile(projectId: string, fileId: string) {
|
||||
return MSR.get(
|
||||
{
|
||||
url: `${GetShareApiDownloadFileUrl}/${projectId}/${fileId}`,
|
||||
responseType: 'blob',
|
||||
},
|
||||
{ isTransformResponse: false }
|
||||
);
|
||||
}
|
||||
// 停止分享导出
|
||||
export function stopShareApiExport(taskId: string) {
|
||||
return MSR.get({ url: `${StopShareApiExportUrl}/${taskId}` });
|
||||
}
|
||||
|
|
|
@ -122,3 +122,6 @@ export const checkSharePsdUrl = '/api/doc/share/check'; // 接口测试-接口
|
|||
export const shareDetailUrl = '/api/doc/share/detail'; // 接口测试-接口管理-查看链接
|
||||
export const shareModuleTreeUrl = '/api/doc/share/module/tree'; // 接口测试-接口管理-模块树
|
||||
export const shareModuleCountUrl = '/api/doc/share/module/count'; // 接口测试-接口管理-模块数量
|
||||
export const ExportShareDefinitionUrl = '/api/doc/share/export'; // 导入分享接口定义
|
||||
export const GetShareApiDownloadFileUrl = '/api/doc/share/download/file'; // 下载导出的文档
|
||||
export const StopShareApiExportUrl = '/api/doc/share/stop'; // 停止分享导出
|
||||
|
|
|
@ -0,0 +1,258 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
:title="t('common.export')"
|
||||
title-align="start"
|
||||
class="ms-modal-upload ms-modal-medium"
|
||||
:width="400"
|
||||
>
|
||||
<div class="mb-[16px] flex gap-[8px]">
|
||||
<div
|
||||
v-for="item of platformList"
|
||||
:key="item.value"
|
||||
:class="`import-item ${exportPlatform === item.value ? 'import-item--active' : ''}`"
|
||||
@click="exportPlatform = item.value"
|
||||
>
|
||||
<div class="text-[var(--color-text-1)]">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="exportPlatform === RequestExportFormat.MeterSphere">
|
||||
<div class="mb-[16px] flex items-center gap-[4px]">
|
||||
<a-switch v-model:model-value="exportApiCase" size="small" />
|
||||
{{ t('apiTestManagement.exportCase') }}
|
||||
</div>
|
||||
<div class="flex items-center gap-[4px]">
|
||||
<a-switch v-model:model-value="exportApiMock" size="small" />
|
||||
{{ t('apiTestManagement.exportMock') }}
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="exportPlatform === RequestExportFormat.SWAGGER" class="text-[var(--color-text-4)]">
|
||||
{{ t('apiTestManagement.exportSwaggerTip') }}
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-end">
|
||||
<a-button type="secondary" :disabled="exportLoading" @click="cancelExport">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button class="ml-3" type="primary" :loading="exportLoading" @click="exportApi">
|
||||
{{ t('common.export') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
|
||||
import MsButton from '@/components/pure/ms-button/index.vue';
|
||||
import type { BatchActionQueryParams } from '@/components/pure/ms-table/type';
|
||||
|
||||
import {
|
||||
exportApiDefinition,
|
||||
exportShareApiDefinition,
|
||||
getApiDownloadFile,
|
||||
getShareApiDownloadFile,
|
||||
stopApiExport,
|
||||
stopShareApiExport,
|
||||
} from '@/api/modules/api-test/management';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useWebsocket from '@/hooks/useWebsocket';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import { downloadByteFile, getGenerateId } from '@/utils';
|
||||
|
||||
import { RequestExportFormat } from '@/enums/apiEnum';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { t } = useI18n();
|
||||
|
||||
const props = defineProps<{
|
||||
batchParams: BatchActionQueryParams;
|
||||
conditionParams: Record<string, any> | (() => Record<string, any>);
|
||||
sorter?: Record<string, any>;
|
||||
isShare?: boolean; // 分享文档id
|
||||
}>();
|
||||
|
||||
const visible = defineModel<boolean>('visible', { required: true });
|
||||
|
||||
const exportPlatform = ref(RequestExportFormat.SWAGGER);
|
||||
const exportApiCase = ref(false);
|
||||
const exportApiMock = ref(false);
|
||||
const exportLoading = ref(false);
|
||||
|
||||
const platformList = [
|
||||
{
|
||||
name: 'Swagger',
|
||||
value: RequestExportFormat.SWAGGER,
|
||||
},
|
||||
{
|
||||
name: 'MeterSphere',
|
||||
value: RequestExportFormat.MeterSphere,
|
||||
},
|
||||
];
|
||||
|
||||
function cancelExport() {
|
||||
visible.value = false;
|
||||
exportPlatform.value = RequestExportFormat.SWAGGER;
|
||||
}
|
||||
|
||||
const websocket = ref<WebSocket>();
|
||||
const reportId = ref('');
|
||||
const isShowExportingMessage = ref(false); // 正在导出提示显示中
|
||||
const exportingMessage = ref();
|
||||
|
||||
const getApiDownload = computed(() => (props.isShare ? getShareApiDownloadFile : getApiDownloadFile));
|
||||
|
||||
// 下载文件
|
||||
async function downloadFile(id: string) {
|
||||
try {
|
||||
const response = await getApiDownload.value(appStore.currentProjectId, id);
|
||||
downloadByteFile(response, 'metersphere-export.json');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// 提示:导出成功
|
||||
function showExportSuccessfulMessage(id: string, count: number) {
|
||||
Message.success({
|
||||
content: () =>
|
||||
h('div', { class: 'flex flex-col gap-[8px] items-start' }, [
|
||||
h('div', { class: 'font-medium' }, t('common.exportSuccessful')),
|
||||
h('div', { class: 'flex items-center gap-[12px]' }, [
|
||||
h('div', t('caseManagement.featureCase.exportApiCount', { number: count })),
|
||||
h(
|
||||
MsButton,
|
||||
{
|
||||
type: 'text',
|
||||
onClick() {
|
||||
downloadFile(id);
|
||||
},
|
||||
},
|
||||
{ default: () => t('common.downloadFile') }
|
||||
),
|
||||
]),
|
||||
]),
|
||||
duration: 10000, // 10s 自动关闭
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
// 开启websocket监听,接收结果
|
||||
async function startWebsocketGetExportResult() {
|
||||
const { createSocket, websocket: _websocket } = useWebsocket({
|
||||
reportId: reportId.value,
|
||||
socketUrl: '/ws/export',
|
||||
onMessage: (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.msgType === 'EXEC_RESULT') {
|
||||
exportingMessage.value.close();
|
||||
reportId.value = data.fileId;
|
||||
// taskId.value = data.taskId;
|
||||
if (data.isSuccessful) {
|
||||
showExportSuccessfulMessage(reportId.value, data.count);
|
||||
} else {
|
||||
Message.error({
|
||||
content: t('common.exportFailed'),
|
||||
duration: 999999999, // 一直展示,除非手动关闭
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
websocket.value?.close();
|
||||
}
|
||||
},
|
||||
});
|
||||
await createSocket();
|
||||
websocket.value = _websocket.value;
|
||||
}
|
||||
|
||||
const stopShareApi = computed(() => (props.isShare ? stopShareApiExport : stopApiExport));
|
||||
// 取消导出
|
||||
async function stopExport(taskId: string) {
|
||||
try {
|
||||
await stopShareApi.value(taskId);
|
||||
exportingMessage.value.close();
|
||||
websocket.value?.close();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// 提示:正在导出
|
||||
function showExportingMessage(taskId: string) {
|
||||
if (isShowExportingMessage.value) return;
|
||||
isShowExportingMessage.value = true;
|
||||
exportingMessage.value = Message.loading({
|
||||
content: () =>
|
||||
h('div', { class: 'flex items-center gap-[12px]' }, [
|
||||
h('div', t('common.exporting')),
|
||||
h(
|
||||
MsButton,
|
||||
{
|
||||
type: 'text',
|
||||
onClick() {
|
||||
stopExport(taskId);
|
||||
},
|
||||
},
|
||||
{ default: () => t('common.cancel') }
|
||||
),
|
||||
]),
|
||||
duration: 999999999, // 一直展示,除非手动关闭
|
||||
closable: true,
|
||||
onClose() {
|
||||
isShowExportingMessage.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const exportApiRequest = computed(() => (props.isShare ? exportShareApiDefinition : exportApiDefinition));
|
||||
/**
|
||||
* 导出接口
|
||||
*/
|
||||
async function exportApi() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
reportId.value = getGenerateId();
|
||||
await startWebsocketGetExportResult();
|
||||
const batchConditionParams =
|
||||
typeof props.conditionParams === 'function' ? await props.conditionParams() : props.conditionParams;
|
||||
const { selectedIds, selectAll, excludeIds } = props.batchParams;
|
||||
const res = await exportApiRequest.value(
|
||||
{
|
||||
selectIds: selectedIds || [],
|
||||
selectAll: !!selectAll,
|
||||
excludeIds: excludeIds || [],
|
||||
...batchConditionParams,
|
||||
exportApiCase: exportApiCase.value,
|
||||
exportApiMock: exportApiMock.value,
|
||||
sort: props.sorter || {},
|
||||
fileId: reportId.value,
|
||||
},
|
||||
exportPlatform.value
|
||||
);
|
||||
showExportingMessage(res);
|
||||
visible.value = false;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
exportLoading.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.import-item {
|
||||
@apply flex cursor-pointer items-center bg-white;
|
||||
|
||||
padding: 8px;
|
||||
width: 150px;
|
||||
border: 1px solid var(--color-text-n8);
|
||||
border-radius: var(--border-radius-small);
|
||||
gap: 6px;
|
||||
}
|
||||
.import-item--active {
|
||||
border: 1px solid rgb(var(--primary-5));
|
||||
background-color: rgb(var(--primary-1));
|
||||
}
|
||||
</style>
|
|
@ -4,6 +4,7 @@
|
|||
:detail="activeApiDetail"
|
||||
:protocols="props.selectedProtocols"
|
||||
@update-follow="activeApiDetail.follow = !activeApiDetail.follow"
|
||||
@export-share="handlerExportShare"
|
||||
/>
|
||||
</div>
|
||||
<div class="doc-toggle-footer">
|
||||
|
@ -91,6 +92,7 @@
|
|||
|
||||
const emit = defineEmits<{
|
||||
(e: 'toggleDetail', type: string): void;
|
||||
(e: 'exportShare', all: boolean): void;
|
||||
}>();
|
||||
|
||||
const localProtocol = localStorage.getItem(ProtocolKeyEnum.API_NEW_PROTOCOL);
|
||||
|
@ -116,6 +118,7 @@
|
|||
rest: [],
|
||||
polymorphicName: '',
|
||||
name: '',
|
||||
num: '',
|
||||
path: '',
|
||||
projectId: '',
|
||||
uploadFileIds: [],
|
||||
|
@ -214,6 +217,10 @@
|
|||
function toggleApiDetail(type: string) {
|
||||
emit('toggleDetail', type);
|
||||
}
|
||||
// 导出分享
|
||||
function handlerExportShare() {
|
||||
emit('exportShare', false);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.apiInfo,
|
||||
|
|
|
@ -11,7 +11,11 @@
|
|||
@refresh="loadApiList(false)"
|
||||
>
|
||||
<template #right>
|
||||
<ShareButton @create="createShare" @show-share-list="showShareList" />
|
||||
<ShareButton
|
||||
v-if="hasAnyPermission(['PROJECT_API_DEFINITION_DOC:READ+SHARE'])"
|
||||
@create="createShare"
|
||||
@show-share-list="showShareList"
|
||||
/>
|
||||
</template>
|
||||
</MsAdvanceFilter>
|
||||
<ms-base-table
|
||||
|
@ -243,47 +247,12 @@
|
|||
@folder-node-select="folderNodeSelect"
|
||||
/>
|
||||
</a-modal>
|
||||
<a-modal
|
||||
<ApiExportModal
|
||||
v-model:visible="showExportModal"
|
||||
:title="t('common.export')"
|
||||
title-align="start"
|
||||
class="ms-modal-upload ms-modal-medium"
|
||||
:width="400"
|
||||
>
|
||||
<div class="mb-[16px] flex gap-[8px]">
|
||||
<div
|
||||
v-for="item of platformList"
|
||||
:key="item.value"
|
||||
:class="`import-item ${exportPlatform === item.value ? 'import-item--active' : ''}`"
|
||||
@click="exportPlatform = item.value"
|
||||
>
|
||||
<div class="text-[var(--color-text-1)]">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<template v-if="exportPlatform === RequestExportFormat.MeterSphere">
|
||||
<div class="mb-[16px] flex items-center gap-[4px]">
|
||||
<a-switch v-model:model-value="exportApiCase" size="small" />
|
||||
{{ t('apiTestManagement.exportCase') }}
|
||||
</div>
|
||||
<div class="flex items-center gap-[4px]">
|
||||
<a-switch v-model:model-value="exportApiMock" size="small" />
|
||||
{{ t('apiTestManagement.exportMock') }}
|
||||
</div>
|
||||
</template>
|
||||
<div v-else-if="exportPlatform === RequestExportFormat.SWAGGER" class="text-[var(--color-text-4)]">
|
||||
{{ t('apiTestManagement.exportSwaggerTip') }}
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="flex justify-end">
|
||||
<a-button type="secondary" :disabled="exportLoading" @click="cancelExport">
|
||||
{{ t('common.cancel') }}
|
||||
</a-button>
|
||||
<a-button class="ml-3" type="primary" :loading="exportLoading" @click="exportApi">
|
||||
{{ t('common.export') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-modal>
|
||||
:batch-params="batchParams"
|
||||
:condition-params="getBatchConditionParams"
|
||||
:sorter="propsRes.sorter || {}"
|
||||
/>
|
||||
<CreateShareModal
|
||||
v-model:visible="showShareModal"
|
||||
:record="editRecord"
|
||||
|
@ -313,6 +282,7 @@
|
|||
import apiMethodName from '@/views/api-test/components/apiMethodName.vue';
|
||||
import apiMethodSelect from '@/views/api-test/components/apiMethodSelect.vue';
|
||||
import apiStatus from '@/views/api-test/components/apiStatus.vue';
|
||||
import ApiExportModal from '@/views/api-test/management/components/management/api/apiExportModal.vue';
|
||||
import moduleTree from '@/views/api-test/management/components/moduleTree.vue';
|
||||
|
||||
import {
|
||||
|
@ -320,20 +290,16 @@
|
|||
batchMoveDefinition,
|
||||
batchUpdateDefinition,
|
||||
deleteDefinition,
|
||||
exportApiDefinition,
|
||||
getApiDownloadFile,
|
||||
getDefinitionPage,
|
||||
sortDefinition,
|
||||
stopApiExport,
|
||||
updateDefinition,
|
||||
} from '@/api/modules/api-test/management';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import useModal from '@/hooks/useModal';
|
||||
import useTableStore from '@/hooks/useTableStore';
|
||||
import useWebsocket from '@/hooks/useWebsocket';
|
||||
import useAppStore from '@/store/modules/app';
|
||||
import useCacheStore from '@/store/modules/cache/cache';
|
||||
import { characterLimit, downloadByteFile, getGenerateId, operationWidth } from '@/utils';
|
||||
import { characterLimit, operationWidth } from '@/utils';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import { ProtocolItem } from '@/models/apiTest/common';
|
||||
|
@ -341,7 +307,7 @@
|
|||
import { ApiDefinitionDetail, ApiDefinitionGetModuleParams } from '@/models/apiTest/management';
|
||||
import { DragSortParams, ModuleTreeNode } from '@/models/common';
|
||||
import { FilterType, ViewTypeEnum } from '@/enums/advancedFilterEnum';
|
||||
import { RequestDefinitionStatus, RequestExportFormat, RequestMethods } from '@/enums/apiEnum';
|
||||
import { RequestDefinitionStatus, RequestMethods } from '@/enums/apiEnum';
|
||||
import { CacheTabTypeEnum } from '@/enums/cacheTabEnum';
|
||||
import { TagUpdateTypeEnum } from '@/enums/commonEnum';
|
||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||
|
@ -1065,164 +1031,6 @@
|
|||
}
|
||||
|
||||
const showExportModal = ref(false);
|
||||
const platformList = [
|
||||
{
|
||||
name: 'Swagger',
|
||||
value: RequestExportFormat.SWAGGER,
|
||||
},
|
||||
{
|
||||
name: 'MeterSphere',
|
||||
value: RequestExportFormat.MeterSphere,
|
||||
},
|
||||
];
|
||||
const exportPlatform = ref(RequestExportFormat.SWAGGER);
|
||||
const exportApiCase = ref(false);
|
||||
const exportApiMock = ref(false);
|
||||
const exportLoading = ref(false);
|
||||
|
||||
function cancelExport() {
|
||||
showExportModal.value = false;
|
||||
exportPlatform.value = RequestExportFormat.SWAGGER;
|
||||
}
|
||||
|
||||
// 下载文件
|
||||
async function downloadFile(id: string) {
|
||||
try {
|
||||
const response = await getApiDownloadFile(appStore.currentProjectId, id);
|
||||
downloadByteFile(response, 'metersphere-export.json');
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// 提示:导出成功
|
||||
function showExportSuccessfulMessage(id: string, count: number) {
|
||||
Message.success({
|
||||
content: () =>
|
||||
h('div', { class: 'flex flex-col gap-[8px] items-start' }, [
|
||||
h('div', { class: 'font-medium' }, t('common.exportSuccessful')),
|
||||
h('div', { class: 'flex items-center gap-[12px]' }, [
|
||||
h('div', t('caseManagement.featureCase.exportApiCount', { number: count })),
|
||||
h(
|
||||
MsButton,
|
||||
{
|
||||
type: 'text',
|
||||
onClick() {
|
||||
downloadFile(id);
|
||||
},
|
||||
},
|
||||
{ default: () => t('common.downloadFile') }
|
||||
),
|
||||
]),
|
||||
]),
|
||||
duration: 10000, // 10s 自动关闭
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
|
||||
const websocket = ref<WebSocket>();
|
||||
const reportId = ref('');
|
||||
const isShowExportingMessage = ref(false); // 正在导出提示显示中
|
||||
const exportingMessage = ref();
|
||||
|
||||
// 开启websocket监听,接收结果
|
||||
async function startWebsocketGetExportResult() {
|
||||
const { createSocket, websocket: _websocket } = useWebsocket({
|
||||
reportId: reportId.value,
|
||||
socketUrl: '/ws/export',
|
||||
onMessage: (event) => {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.msgType === 'EXEC_RESULT') {
|
||||
exportingMessage.value.close();
|
||||
reportId.value = data.fileId;
|
||||
// taskId.value = data.taskId;
|
||||
if (data.isSuccessful) {
|
||||
showExportSuccessfulMessage(reportId.value, data.count);
|
||||
} else {
|
||||
Message.error({
|
||||
content: t('common.exportFailed'),
|
||||
duration: 999999999, // 一直展示,除非手动关闭
|
||||
closable: true,
|
||||
});
|
||||
}
|
||||
websocket.value?.close();
|
||||
}
|
||||
},
|
||||
});
|
||||
await createSocket();
|
||||
websocket.value = _websocket.value;
|
||||
}
|
||||
|
||||
// 取消导出
|
||||
async function stopExport(taskId: string) {
|
||||
try {
|
||||
await stopApiExport(taskId);
|
||||
exportingMessage.value.close();
|
||||
websocket.value?.close();
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
// 提示:正在导出
|
||||
function showExportingMessage(taskId: string) {
|
||||
if (isShowExportingMessage.value) return;
|
||||
isShowExportingMessage.value = true;
|
||||
exportingMessage.value = Message.loading({
|
||||
content: () =>
|
||||
h('div', { class: 'flex items-center gap-[12px]' }, [
|
||||
h('div', t('common.exporting')),
|
||||
h(
|
||||
MsButton,
|
||||
{
|
||||
type: 'text',
|
||||
onClick() {
|
||||
stopExport(taskId);
|
||||
},
|
||||
},
|
||||
{ default: () => t('common.cancel') }
|
||||
),
|
||||
]),
|
||||
duration: 999999999, // 一直展示,除非手动关闭
|
||||
closable: true,
|
||||
onClose() {
|
||||
isShowExportingMessage.value = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出接口
|
||||
*/
|
||||
async function exportApi() {
|
||||
try {
|
||||
exportLoading.value = true;
|
||||
reportId.value = getGenerateId();
|
||||
await startWebsocketGetExportResult();
|
||||
const batchConditionParams = await getBatchConditionParams();
|
||||
const res = await exportApiDefinition(
|
||||
{
|
||||
selectIds: tableSelected.value as string[],
|
||||
selectAll: !!batchParams.value?.selectAll,
|
||||
excludeIds: batchParams.value?.excludeIds || [],
|
||||
...batchConditionParams,
|
||||
exportApiCase: exportApiCase.value,
|
||||
exportApiMock: exportApiMock.value,
|
||||
sort: propsRes.value.sorter || {},
|
||||
fileId: reportId.value,
|
||||
},
|
||||
exportPlatform.value
|
||||
);
|
||||
|
||||
showExportingMessage(res);
|
||||
showExportModal.value = false;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error);
|
||||
} finally {
|
||||
exportLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理表格选中后批量操作
|
||||
|
@ -1323,19 +1131,6 @@
|
|||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.import-item {
|
||||
@apply flex cursor-pointer items-center bg-white;
|
||||
|
||||
padding: 8px;
|
||||
width: 150px;
|
||||
border: 1px solid var(--color-text-n8);
|
||||
border-radius: var(--border-radius-small);
|
||||
gap: 6px;
|
||||
}
|
||||
.import-item--active {
|
||||
border: 1px solid rgb(var(--primary-5));
|
||||
background-color: rgb(var(--primary-1));
|
||||
}
|
||||
:deep(.param-input:not(.arco-input-focus, .arco-select-view-focus)) {
|
||||
&:not(:hover) {
|
||||
border-color: transparent !important;
|
||||
|
|
|
@ -74,11 +74,12 @@
|
|||
:validate-trigger="['blur']"
|
||||
:rules="form.isPrivate ? [{ validator: validatePassword }] : []"
|
||||
>
|
||||
<a-input
|
||||
<a-input-password
|
||||
v-model:model-value="form.password"
|
||||
class="w-[240px]"
|
||||
:max-length="6"
|
||||
:placeholder="t('apiTestManagement.enterPassword')"
|
||||
autocomplete="new-password"
|
||||
/>
|
||||
</a-form-item>
|
||||
<div class="mb-[16px] flex items-center">
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
detail: RequestParam;
|
||||
protocols: ProtocolItem[];
|
||||
}>();
|
||||
const emit = defineEmits(['updateFollow']);
|
||||
const emit = defineEmits(['updateFollow', 'exportShare']);
|
||||
|
||||
const { copy, isSupported } = useClipboard({ legacy: true });
|
||||
const { t } = useI18n();
|
||||
|
@ -189,8 +189,10 @@
|
|||
}
|
||||
|
||||
const activeKey = ref('detail');
|
||||
// 导出分享 TODO 等待联调
|
||||
function exportShare() {}
|
||||
// 导出分享
|
||||
function exportShare() {
|
||||
emit('exportShare');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
@ -9,7 +9,12 @@
|
|||
>
|
||||
<div class="p-[16px]">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<a-button class="w-[84px]" type="primary" @click="emit('editOrCreate')">
|
||||
<a-button
|
||||
v-permission="['PROJECT_API_DEFINITION_DOC:READ+SHARE']"
|
||||
class="w-[84px]"
|
||||
type="primary"
|
||||
@click="emit('editOrCreate')"
|
||||
>
|
||||
{{ t('apiTestManagement.newCreateShare') }}
|
||||
</a-button>
|
||||
<a-input-search
|
||||
|
@ -27,19 +32,21 @@
|
|||
{{ record.isPrivate ? t('apiTestManagement.passwordView') : t('apiTestManagement.publicityView') }}
|
||||
</template>
|
||||
<template #operation="{ record }">
|
||||
<a-tooltip :disabled="!!record.apiShareNum" :content="t('apiTestManagement.apiShareNumberTip')">
|
||||
<MsButton class="!mx-0" :disabled="!record.apiShareNum" @click="viewLink(record)">
|
||||
{{ t('apiTestManagement.viewLink') }}
|
||||
<div v-permission="['PROJECT_API_DEFINITION_DOC:READ+SHARE']" class="flex items-center">
|
||||
<a-tooltip :disabled="!!record.apiShareNum" :content="t('apiTestManagement.apiShareNumberTip')">
|
||||
<MsButton class="!mx-0" :disabled="!record.apiShareNum" @click="viewLink(record)">
|
||||
{{ t('apiTestManagement.viewLink') }}
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
<a-divider direction="vertical" :margin="8" />
|
||||
<MsButton class="!mx-0" @click="editShare(record)">
|
||||
{{ t('common.edit') }}
|
||||
</MsButton>
|
||||
</a-tooltip>
|
||||
<a-divider direction="vertical" :margin="8" />
|
||||
<MsButton class="!mx-0" @click="editShare(record)">
|
||||
{{ t('common.edit') }}
|
||||
</MsButton>
|
||||
<a-divider direction="vertical" :margin="8" />
|
||||
<MsButton class="!mx-0" @click="deleteHandler(record)">
|
||||
{{ t('common.delete') }}
|
||||
</MsButton>
|
||||
<a-divider direction="vertical" :margin="8" />
|
||||
<MsButton class="!mx-0" @click="deleteHandler(record)">
|
||||
{{ t('common.delete') }}
|
||||
</MsButton>
|
||||
</div>
|
||||
</template>
|
||||
</MsBaseTable>
|
||||
</div>
|
||||
|
@ -62,7 +69,8 @@
|
|||
import useModal from '@/hooks/useModal';
|
||||
import useOpenNewPage from '@/hooks/useOpenNewPage';
|
||||
import { useAppStore, useTableStore } from '@/store';
|
||||
import { characterLimit } from '@/utils';
|
||||
import { characterLimit, operationWidth } from '@/utils';
|
||||
import { hasAnyPermission } from '@/utils/permission';
|
||||
|
||||
import type { ShareDetail, shareItem } from '@/models/apiTest/management';
|
||||
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||
|
@ -131,11 +139,11 @@
|
|||
showDrag: true,
|
||||
},
|
||||
{
|
||||
title: 'common.operation',
|
||||
title: hasAnyPermission(['PROJECT_API_DEFINITION_DOC:READ+SHARE']) ? 'common.operation' : '',
|
||||
slotName: 'operation',
|
||||
dataIndex: 'operation',
|
||||
fixed: 'right',
|
||||
width: 180,
|
||||
width: operationWidth(215, hasAnyPermission(['PROJECT_API_DEFINITION_DOC:READ+SHARE']) ? 180 : 50),
|
||||
showInTable: true,
|
||||
showDrag: false,
|
||||
},
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
/>
|
||||
</MsButton>
|
||||
</popConfirm>
|
||||
<a-tooltip v-if="props.docShareId && shareDetailInfo?.allowExport" :content="t('common.export')">
|
||||
<a-tooltip v-if="props.docShareId && shareDetailInfo.allowExport" :content="t('common.export')">
|
||||
<MsButton type="icon" status="secondary" class="!mr-[4px] p-[4px]" @click="changeApiExpand">
|
||||
<MsIcon type="icon-icon_top-align_outlined" :size="16" @click="exportShare" />
|
||||
</MsButton>
|
||||
|
@ -356,7 +356,14 @@
|
|||
moduleIds: [],
|
||||
});
|
||||
// 分享详情
|
||||
const shareDetailInfo = inject<Ref<ShareDetailType>>('shareDetailInfo');
|
||||
const shareDetailInfo = inject<Ref<ShareDetailType>>(
|
||||
'shareDetailInfo',
|
||||
ref({
|
||||
invalid: false,
|
||||
allowExport: false,
|
||||
isPrivate: false,
|
||||
})
|
||||
);
|
||||
|
||||
const apiNodes = ref<TreeNode<ModuleTreeNode>[]>([]);
|
||||
const currentNode = ref<TreeNode<ModuleTreeNode> | null>(null);
|
||||
|
@ -747,7 +754,7 @@
|
|||
|
||||
// 导出分享
|
||||
function exportShare() {
|
||||
emit('exportShare');
|
||||
emit('exportShare', true);
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
@delete-node="handleDeleteApiFromModuleTree"
|
||||
@execute="handleExecute"
|
||||
@open-current-node="openCurrentNode"
|
||||
@export-share="handleExportShare"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="!docShareId" class="flex-1">
|
||||
|
@ -66,6 +67,7 @@
|
|||
:previous-node="previousNode"
|
||||
:next-node="nextNode"
|
||||
@toggle-detail="toggleDetail"
|
||||
@export-share="handleExportShare"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -107,6 +109,12 @@
|
|||
</a-button>
|
||||
</template>
|
||||
</a-modal>
|
||||
<ApiExportModal
|
||||
v-model:visible="showExportModal"
|
||||
:batch-params="batchParams"
|
||||
:condition-params="getConditionParams"
|
||||
is-share
|
||||
/>
|
||||
</MsCard>
|
||||
</template>
|
||||
|
||||
|
@ -120,10 +128,12 @@
|
|||
|
||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||
import MsSplitBox from '@/components/pure/ms-split-box/index.vue';
|
||||
import type { BatchActionQueryParams } from '@/components/pure/ms-table/type';
|
||||
import { RequestParam } from '../components/requestComposition/index.vue';
|
||||
import importApi from './components/import.vue';
|
||||
import management from './components/management/index.vue';
|
||||
import moduleTree from './components/moduleTree.vue';
|
||||
import ApiExportModal from '@/views/api-test/management/components/management/api/apiExportModal.vue';
|
||||
import ApiSharePreview from '@/views/api-test/management/components/management/api/apiSharePreview.vue';
|
||||
|
||||
import { getProtocolList } from '@/api/modules/api-test/common';
|
||||
|
@ -318,10 +328,42 @@
|
|||
formRef.value?.resetFields();
|
||||
checkForm.value.password = '';
|
||||
}
|
||||
|
||||
const shareDetailInfo = ref<ShareDetailType>();
|
||||
const currentNode = ref();
|
||||
|
||||
const showExportModal = ref(false);
|
||||
const batchParams = ref<BatchActionQueryParams>({
|
||||
selectedIds: [],
|
||||
selectAll: false,
|
||||
excludeIds: [],
|
||||
});
|
||||
|
||||
// 导出全部|导出单个
|
||||
function handleExportShare(all: boolean) {
|
||||
batchParams.value.selectAll = all;
|
||||
batchParams.value.selectedIds = all ? [] : [currentNode.value?.id];
|
||||
showExportModal.value = true;
|
||||
}
|
||||
|
||||
function getConditionParams() {
|
||||
return {
|
||||
condition: {
|
||||
keyword: '',
|
||||
filter: {},
|
||||
viewId: '',
|
||||
},
|
||||
projectId: appStore.currentProjectId,
|
||||
protocols: selectedProtocols.value,
|
||||
moduleIds: [],
|
||||
shareId: docShareId.value,
|
||||
};
|
||||
}
|
||||
|
||||
const shareDetailInfo = ref<ShareDetailType>({
|
||||
invalid: false,
|
||||
allowExport: false,
|
||||
isPrivate: false,
|
||||
});
|
||||
|
||||
// 获取分享详情
|
||||
async function getShareDetail() {
|
||||
try {
|
||||
|
@ -393,7 +435,7 @@
|
|||
provide('refreshModuleTreeCount', refreshModuleTreeCount);
|
||||
provide('folderTreePathMap', folderTreePathMap.value);
|
||||
provide('docShareId', docShareId.value);
|
||||
provide('shareDetailInfo', shareDetailInfo.value);
|
||||
provide('shareDetailInfo', shareDetailInfo);
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
Loading…
Reference in New Issue