feat(接口测试): 接口测试文档分享详情页面拆分不展示菜单
This commit is contained in:
parent
946ed7de55
commit
c0f4b89742
|
@ -108,6 +108,7 @@ export enum SettingRouteEnum {
|
||||||
export enum ShareEnum {
|
export enum ShareEnum {
|
||||||
SHARE = 'share',
|
SHARE = 'share',
|
||||||
SHARE_REPORT_SCENARIO = 'shareReportScenario',
|
SHARE_REPORT_SCENARIO = 'shareReportScenario',
|
||||||
|
SHARE_DEFINITION_API = 'shareDefinitionApi',
|
||||||
SHARE_REPORT_CASE = 'shareReportCase',
|
SHARE_REPORT_CASE = 'shareReportCase',
|
||||||
SHARE_REPORT_TEST_PLAN = 'shareReportTestPlan',
|
SHARE_REPORT_TEST_PLAN = 'shareReportTestPlan',
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,11 @@ export const WHITE_LIST = [
|
||||||
path: '/shareReportTestPlan',
|
path: '/shareReportTestPlan',
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'shareDefinitionApi',
|
||||||
|
path: '/shareDefinitionApi',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -38,6 +43,11 @@ export const WHITE_LIST = [
|
||||||
path: '/shareReportTestPlan',
|
path: '/shareReportTestPlan',
|
||||||
children: [],
|
children: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'shareDefinitionApi',
|
||||||
|
path: '/shareDefinitionApi',
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// 左侧菜单底部对齐的菜单数组,数组项为一级路由的name
|
// 左侧菜单底部对齐的菜单数组,数组项为一级路由的name
|
||||||
|
|
|
@ -46,6 +46,17 @@ const ShareRoute: AppRouteRecordRaw = {
|
||||||
isTopMenu: false,
|
isTopMenu: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// 接口文档分享
|
||||||
|
{
|
||||||
|
path: 'shareDefinitionApi',
|
||||||
|
name: ShareEnum.SHARE_DEFINITION_API,
|
||||||
|
component: () => import('@/views/api-test/management/components/management/api/shareApiDocIndex.vue'),
|
||||||
|
meta: {
|
||||||
|
locale: '',
|
||||||
|
roles: ['*'],
|
||||||
|
isTopMenu: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="h-[calc(100%-32px)]">
|
<div class="h-[calc(100%-64px)]">
|
||||||
<ApiPreview
|
<ApiPreview
|
||||||
:detail="activeApiDetail"
|
:detail="activeApiDetail"
|
||||||
:protocols="props.selectedProtocols"
|
:protocols="props.selectedProtocols"
|
||||||
|
@ -238,7 +238,6 @@
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
height: 22px;
|
|
||||||
|
|
||||||
@apply flex w-full items-center justify-between bg-white;
|
@apply flex w-full items-center justify-between bg-white;
|
||||||
.doc-toggle {
|
.doc-toggle {
|
||||||
|
|
|
@ -0,0 +1,320 @@
|
||||||
|
<template>
|
||||||
|
<MsCard simple no-content-padding auto-height>
|
||||||
|
<div class="h-[calc(100vh-32px)]">
|
||||||
|
<MsSplitBox :size="300" :max="0.5">
|
||||||
|
<template #first>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="p-[16px]">
|
||||||
|
<moduleTree
|
||||||
|
ref="moduleTreeRef"
|
||||||
|
:active-node-id="activeNodeId"
|
||||||
|
:doc-share-id="docShareId"
|
||||||
|
@init="handleModuleInit"
|
||||||
|
@folder-node-select="handleNodeSelect"
|
||||||
|
@change-protocol="handleProtocolChange"
|
||||||
|
@open-current-node="openCurrentNode"
|
||||||
|
@export-share="handleExportShare"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #second>
|
||||||
|
<ApiSharePreview
|
||||||
|
:selected-protocols="protocols"
|
||||||
|
:api-info="currentNode"
|
||||||
|
:previous-node="previousNode"
|
||||||
|
:next-node="nextNode"
|
||||||
|
@toggle-detail="toggleDetail"
|
||||||
|
@export-share="handleExportShare"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</MsSplitBox>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分享密码校验 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="checkPsdModal"
|
||||||
|
:mask-closable="false"
|
||||||
|
:closable="false"
|
||||||
|
:mask="true"
|
||||||
|
title-align="start"
|
||||||
|
class="ms-modal-upload ms-modal-medium ms-modal-share"
|
||||||
|
:width="280"
|
||||||
|
unmount-on-close
|
||||||
|
@close="closeShareHandler"
|
||||||
|
>
|
||||||
|
<div class="no-resource-svg"></div>
|
||||||
|
<a-form ref="formRef" :rules="rules" :model="checkForm" layout="vertical">
|
||||||
|
<a-form-item
|
||||||
|
class="password-form mb-0"
|
||||||
|
field="password"
|
||||||
|
:label="t('apiTestManagement.effectiveTime')"
|
||||||
|
hide-asterisk
|
||||||
|
hide-label
|
||||||
|
:validate-trigger="['blur']"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model="checkForm.password"
|
||||||
|
:max-length="6"
|
||||||
|
:placeholder="t('apiTestManagement.sharePasswordPlaceholder')"
|
||||||
|
allow-clear
|
||||||
|
autocomplete="new-password"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<template #footer>
|
||||||
|
<a-button type="primary" :loading="checkLoading" :disabled="!checkForm.password" @click="handleCheckPsd">
|
||||||
|
{{ t('common.confirm') }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
</a-modal>
|
||||||
|
<ApiExportModal
|
||||||
|
v-model:visible="showExportModal"
|
||||||
|
:batch-params="batchParams"
|
||||||
|
:condition-params="getConditionParams"
|
||||||
|
is-share
|
||||||
|
/>
|
||||||
|
</MsCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { FormInstance, Message } from '@arco-design/web-vue';
|
||||||
|
|
||||||
|
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 ApiExportModal from '@/views/api-test/management/components/management/api/apiExportModal.vue';
|
||||||
|
import ApiSharePreview from '@/views/api-test/management/components/management/api/apiSharePreview.vue';
|
||||||
|
import moduleTree from '@/views/api-test/management/components/moduleTree.vue';
|
||||||
|
|
||||||
|
import { getProtocolList } from '@/api/modules/api-test/common';
|
||||||
|
import { checkSharePsd, shareDetail } from '@/api/modules/api-test/management';
|
||||||
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
|
import { NOT_FOUND_RESOURCE } from '@/router/constants';
|
||||||
|
import { useUserStore } from '@/store';
|
||||||
|
import useDocShareCheckStore from '@/store/modules/api/docShareCheck';
|
||||||
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
|
import { ShareDetailType } from '@/models/apiTest/management';
|
||||||
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
|
|
||||||
|
const appStore = useAppStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const docCheckStore = useDocShareCheckStore();
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
|
const activeNodeId = ref<string | number>('all');
|
||||||
|
const activeModule = ref<string>('all');
|
||||||
|
const offspringIds = ref<string[]>([]);
|
||||||
|
|
||||||
|
const docShareId = ref<string>(route.query.docShareId as string);
|
||||||
|
|
||||||
|
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||||
|
const selectedProtocols = ref<string[]>([]);
|
||||||
|
const folderTreePathMap = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
function handleModuleInit(tree: ModuleTreeNode[], _protocols: string[], pathMap: Record<string, any>) {
|
||||||
|
folderTree.value = tree;
|
||||||
|
selectedProtocols.value = _protocols;
|
||||||
|
folderTreePathMap.value = pathMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleNodeSelect(keys: string[], _offspringIds: string[]) {
|
||||||
|
[activeModule.value] = keys;
|
||||||
|
offspringIds.value = _offspringIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleProtocolChange(val: string[]) {
|
||||||
|
selectedProtocols.value = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkLoading = ref<boolean>(false);
|
||||||
|
const checkPsdModal = ref<boolean>(false);
|
||||||
|
const checkForm = ref({
|
||||||
|
docShareId: route.query.docShareId as string,
|
||||||
|
password: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const validatePassword = (value: string | undefined, callback: (error?: string) => void) => {
|
||||||
|
const sixDigitRegex = /^\d{6}$/;
|
||||||
|
|
||||||
|
if (value === undefined || value === '') {
|
||||||
|
callback(t('apiTestManagement.enterPassword'));
|
||||||
|
} else if (!sixDigitRegex.test(value)) {
|
||||||
|
callback(t('apiTestManagement.enterPassword'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
password: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: t('apiTestManagement.sharePasswordPlaceholder'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: validatePassword,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const moduleTreeRef = ref<InstanceType<typeof moduleTree>>();
|
||||||
|
|
||||||
|
// 上一条|下一条
|
||||||
|
function toggleDetail(type: string) {
|
||||||
|
if (type === 'prev') {
|
||||||
|
moduleTreeRef.value?.previousApi();
|
||||||
|
} else {
|
||||||
|
moduleTreeRef.value?.nextApi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
// 关闭分享
|
||||||
|
function closeShareHandler() {
|
||||||
|
checkPsdModal.value = false;
|
||||||
|
formRef.value?.resetFields();
|
||||||
|
checkForm.value.password = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const protocols = ref<any[]>([]);
|
||||||
|
async function initProtocolList() {
|
||||||
|
try {
|
||||||
|
protocols.value = await getProtocolList(appStore.currentOrgId);
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
initProtocolList();
|
||||||
|
});
|
||||||
|
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 {
|
||||||
|
shareDetailInfo.value = await shareDetail(docShareId.value);
|
||||||
|
// 资源无效
|
||||||
|
if (shareDetailInfo.value.invalid) {
|
||||||
|
router.push({
|
||||||
|
name: NOT_FOUND_RESOURCE,
|
||||||
|
query: {
|
||||||
|
type: 'EXPIRED',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 限制访问校验
|
||||||
|
if (shareDetailInfo.value.isPrivate && !docCheckStore.isDocVerified(docShareId.value, userStore.id || '')) {
|
||||||
|
checkPsdModal.value = true;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousNode = ref<ModuleTreeNode | null>();
|
||||||
|
const nextNode = ref<ModuleTreeNode | null>();
|
||||||
|
|
||||||
|
// 设置当前预览节点
|
||||||
|
function openCurrentNode(node: ModuleTreeNode, apiNodes: ModuleTreeNode[]) {
|
||||||
|
const index = apiNodes.indexOf(node);
|
||||||
|
currentNode.value = node;
|
||||||
|
previousNode.value = index > 0 ? apiNodes[index - 1] : null;
|
||||||
|
nextNode.value = index < apiNodes.length - 1 ? apiNodes[index + 1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 校验密码
|
||||||
|
function handleCheckPsd() {
|
||||||
|
formRef.value?.validate(async (errors) => {
|
||||||
|
if (!errors) {
|
||||||
|
try {
|
||||||
|
checkLoading.value = true;
|
||||||
|
const res = await checkSharePsd(checkForm.value);
|
||||||
|
if (res) {
|
||||||
|
closeShareHandler();
|
||||||
|
// 标记为已验证
|
||||||
|
docCheckStore.markDocAsVerified(docShareId.value, userStore.id || '');
|
||||||
|
checkPsdModal.value = false;
|
||||||
|
} else {
|
||||||
|
Message.error(t('apiTestManagement.apiSharePsdError'));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(error);
|
||||||
|
} finally {
|
||||||
|
checkLoading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
if (docShareId.value) {
|
||||||
|
getShareDetail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
provide('docShareId', docShareId.value);
|
||||||
|
provide('shareDetailInfo', shareDetailInfo);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.no-resource-svg {
|
||||||
|
margin: 0 auto 24px;
|
||||||
|
width: 160px;
|
||||||
|
height: 98px;
|
||||||
|
background: url('@/assets/svg/no_resource.svg');
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
:deep(.ms-modal-share) {
|
||||||
|
.arco-modal-mask {
|
||||||
|
background: var(--color-text-1) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
:deep(.password-form) {
|
||||||
|
.arco-form-item-message {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -67,11 +67,11 @@
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
|
|
||||||
import type { shareItem } from '@/models/apiTest/management';
|
import type { shareItem } from '@/models/apiTest/management';
|
||||||
|
import { RouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
|
|
||||||
const { copy, isSupported } = useClipboard({ legacy: true });
|
const { copy, isSupported } = useClipboard({ legacy: true });
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
|
@ -133,9 +133,9 @@
|
||||||
}
|
}
|
||||||
if (isSupported) {
|
if (isSupported) {
|
||||||
// 判断路由中是不是有dId参数,有的话只是修改当前的值就可以了
|
// 判断路由中是不是有dId参数,有的话只是修改当前的值就可以了
|
||||||
const url = window.location.href;
|
const shareLinkUrl = `${origin}/#/${RouteEnum.SHARE}/${RouteEnum.SHARE_DEFINITION_API}`;
|
||||||
const dIdParam = `&docShareId=${item.id}`;
|
const dIdParam = `?orgId=${appStore.currentOrgId}&pId=${appStore.currentProjectId}&docShareId=${item.id}`;
|
||||||
copy(`${url}${dIdParam}`);
|
copy(`${shareLinkUrl}${dIdParam}`);
|
||||||
Message.success(t('apiTestManagement.shareUrlCopied'));
|
Message.success(t('apiTestManagement.shareUrlCopied'));
|
||||||
} else {
|
} else {
|
||||||
Message.error(t('common.copyNotSupport'));
|
Message.error(t('common.copyNotSupport'));
|
||||||
|
|
|
@ -78,7 +78,7 @@
|
||||||
import { hasAnyPermission } from '@/utils/permission';
|
import { hasAnyPermission } from '@/utils/permission';
|
||||||
|
|
||||||
import type { ShareDetail, shareItem } from '@/models/apiTest/management';
|
import type { ShareDetail, shareItem } from '@/models/apiTest/management';
|
||||||
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
import { ShareEnum } from '@/enums/routeEnum';
|
||||||
import { TableKeyEnum } from '@/enums/tableEnum';
|
import { TableKeyEnum } from '@/enums/tableEnum';
|
||||||
import { FilterRemoteMethodsEnum } from '@/enums/tableFilterEnum';
|
import { FilterRemoteMethodsEnum } from '@/enums/tableFilterEnum';
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@
|
||||||
|
|
||||||
// 查看链接
|
// 查看链接
|
||||||
function viewLink(record: shareItem) {
|
function viewLink(record: shareItem) {
|
||||||
openNewPage(ApiTestRouteEnum.API_TEST_MANAGEMENT, {
|
openNewPage(ShareEnum.SHARE_DEFINITION_API, {
|
||||||
docShareId: record.id,
|
docShareId: record.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,14 +268,14 @@
|
||||||
const virtualListProps = computed(() => {
|
const virtualListProps = computed(() => {
|
||||||
if (props.readOnly || props.isModal) {
|
if (props.readOnly || props.isModal) {
|
||||||
return {
|
return {
|
||||||
height: props.docShareId ? 'calc(60vh - 150px)' : 'calc(60vh - 190px)',
|
height: 'calc(60vh - 190px)',
|
||||||
threshold: 200,
|
threshold: 200,
|
||||||
fixedSize: true,
|
fixedSize: true,
|
||||||
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
height: props.docShareId ? 'calc(100vh - 233px)' : 'calc(100vh - 273px)',
|
height: props.docShareId ? 'calc(100vh - 150px)' : 'calc(100vh - 273px)',
|
||||||
threshold: 200,
|
threshold: 200,
|
||||||
fixedSize: true,
|
fixedSize: true,
|
||||||
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
buffer: 15, // 缓冲区默认 10 的时候,虚拟滚动的底部 padding 计算有问题
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
<moduleTree
|
<moduleTree
|
||||||
ref="moduleTreeRef"
|
ref="moduleTreeRef"
|
||||||
:active-node-id="activeNodeId"
|
:active-node-id="activeNodeId"
|
||||||
:doc-share-id="docShareId"
|
|
||||||
@init="handleModuleInit"
|
@init="handleModuleInit"
|
||||||
@new-api="newApi"
|
@new-api="newApi"
|
||||||
@import="importDrawerVisible = true"
|
@import="importDrawerVisible = true"
|
||||||
|
@ -17,8 +16,6 @@
|
||||||
@update-api-node="handleUpdateApiNode"
|
@update-api-node="handleUpdateApiNode"
|
||||||
@delete-node="handleDeleteApiFromModuleTree"
|
@delete-node="handleDeleteApiFromModuleTree"
|
||||||
@execute="handleExecute"
|
@execute="handleExecute"
|
||||||
@open-current-node="openCurrentNode"
|
|
||||||
@export-share="handleExportShare"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!docShareId" class="flex-1">
|
<div v-if="!docShareId" class="flex-1">
|
||||||
|
@ -36,7 +33,6 @@
|
||||||
<template #second>
|
<template #second>
|
||||||
<div class="relative flex h-full flex-col">
|
<div class="relative flex h-full flex-col">
|
||||||
<div
|
<div
|
||||||
v-if="!docShareId"
|
|
||||||
id="managementContainer"
|
id="managementContainer"
|
||||||
:class="['absolute z-[102] h-full w-full', importDrawerVisible ? '' : 'invisible']"
|
:class="['absolute z-[102] h-full w-full', importDrawerVisible ? '' : 'invisible']"
|
||||||
style="transition: all 0.3s"
|
style="transition: all 0.3s"
|
||||||
|
@ -51,7 +47,6 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<management
|
<management
|
||||||
v-if="!docShareId"
|
|
||||||
ref="managementRef"
|
ref="managementRef"
|
||||||
:module-tree="folderTree"
|
:module-tree="folderTree"
|
||||||
:active-module="activeModule"
|
:active-module="activeModule"
|
||||||
|
@ -60,62 +55,9 @@
|
||||||
@import="importDrawerVisible = true"
|
@import="importDrawerVisible = true"
|
||||||
@handle-adv-search="handleAdvSearch"
|
@handle-adv-search="handleAdvSearch"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ApiSharePreview
|
|
||||||
v-if="docShareId"
|
|
||||||
:selected-protocols="protocols"
|
|
||||||
:api-info="currentNode"
|
|
||||||
:previous-node="previousNode"
|
|
||||||
:next-node="nextNode"
|
|
||||||
@toggle-detail="toggleDetail"
|
|
||||||
@export-share="handleExportShare"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MsSplitBox>
|
</MsSplitBox>
|
||||||
<!-- 分享密码校验 -->
|
|
||||||
<a-modal
|
|
||||||
v-model:visible="checkPsdModal"
|
|
||||||
:mask-closable="false"
|
|
||||||
:closable="false"
|
|
||||||
:mask="true"
|
|
||||||
title-align="start"
|
|
||||||
class="ms-modal-upload ms-modal-medium ms-modal-share"
|
|
||||||
:width="280"
|
|
||||||
unmount-on-close
|
|
||||||
@close="closeShareHandler"
|
|
||||||
>
|
|
||||||
<div class="no-resource-svg"></div>
|
|
||||||
<a-form ref="formRef" :rules="rules" :model="checkForm" layout="vertical">
|
|
||||||
<a-form-item
|
|
||||||
class="password-form mb-0"
|
|
||||||
field="password"
|
|
||||||
:label="t('apiTestManagement.effectiveTime')"
|
|
||||||
hide-asterisk
|
|
||||||
hide-label
|
|
||||||
:validate-trigger="['blur']"
|
|
||||||
>
|
|
||||||
<a-input-password
|
|
||||||
v-model="checkForm.password"
|
|
||||||
:max-length="6"
|
|
||||||
:placeholder="t('apiTestManagement.sharePasswordPlaceholder')"
|
|
||||||
allow-clear
|
|
||||||
autocomplete="new-password"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
<template #footer>
|
|
||||||
<a-button type="primary" :loading="checkLoading" :disabled="!checkForm.password" @click="handleCheckPsd">
|
|
||||||
{{ t('common.confirm') }}
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
</a-modal>
|
|
||||||
<ApiExportModal
|
|
||||||
v-model:visible="showExportModal"
|
|
||||||
:batch-params="batchParams"
|
|
||||||
:condition-params="getConditionParams"
|
|
||||||
is-share
|
|
||||||
/>
|
|
||||||
</MsCard>
|
</MsCard>
|
||||||
<importTaskDrawer v-model:visible="taskDrawerVisible" />
|
<importTaskDrawer v-model:visible="taskDrawerVisible" />
|
||||||
</template>
|
</template>
|
||||||
|
@ -126,28 +68,20 @@
|
||||||
*/
|
*/
|
||||||
import { provide } from 'vue';
|
import { provide } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { FormInstance, Message } from '@arco-design/web-vue';
|
|
||||||
|
|
||||||
import MsCard from '@/components/pure/ms-card/index.vue';
|
import MsCard from '@/components/pure/ms-card/index.vue';
|
||||||
import MsSplitBox from '@/components/pure/ms-split-box/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 { RequestParam } from '../components/requestComposition/index.vue';
|
||||||
import importApi from './components/import.vue';
|
import importApi from './components/import.vue';
|
||||||
import importTaskDrawer from './components/importTaskDrawer.vue';
|
import importTaskDrawer from './components/importTaskDrawer.vue';
|
||||||
import management from './components/management/index.vue';
|
import management from './components/management/index.vue';
|
||||||
import moduleTree from './components/moduleTree.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';
|
import { getTrashModuleCount } from '@/api/modules/api-test/management';
|
||||||
import { checkSharePsd, getTrashModuleCount, shareDetail } from '@/api/modules/api-test/management';
|
|
||||||
import { useI18n } from '@/hooks/useI18n';
|
import { useI18n } from '@/hooks/useI18n';
|
||||||
import { NOT_FOUND_RESOURCE } from '@/router/constants';
|
|
||||||
import { useUserStore } from '@/store';
|
|
||||||
import useDocShareCheckStore from '@/store/modules/api/docShareCheck';
|
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
|
|
||||||
import { ApiDefinitionGetModuleParams, ShareDetailType } from '@/models/apiTest/management';
|
import { ApiDefinitionGetModuleParams } from '@/models/apiTest/management';
|
||||||
import { ModuleTreeNode } from '@/models/common';
|
import { ModuleTreeNode } from '@/models/common';
|
||||||
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
import { ApiTestRouteEnum } from '@/enums/routeEnum';
|
||||||
|
|
||||||
|
@ -155,8 +89,6 @@
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const docCheckStore = useDocShareCheckStore();
|
|
||||||
const userStore = useUserStore();
|
|
||||||
|
|
||||||
const activeModule = ref<string>('all');
|
const activeModule = ref<string>('all');
|
||||||
const folderTree = ref<ModuleTreeNode[]>([]);
|
const folderTree = ref<ModuleTreeNode[]>([]);
|
||||||
|
@ -190,20 +122,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const protocols = ref<any[]>([]);
|
|
||||||
async function initProtocolList() {
|
|
||||||
try {
|
|
||||||
protocols.value = await getProtocolList(appStore.currentOrgId);
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
initProtocolList();
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleProtocolChange(val: string[]) {
|
function handleProtocolChange(val: string[]) {
|
||||||
selectedProtocols.value = val;
|
selectedProtocols.value = val;
|
||||||
}
|
}
|
||||||
|
@ -284,153 +202,8 @@
|
||||||
moduleTreeRef.value?.setActiveFolder('all');
|
moduleTreeRef.value?.setActiveFolder('all');
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkLoading = ref<boolean>(false);
|
|
||||||
const checkPsdModal = ref<boolean>(false);
|
|
||||||
const checkForm = ref({
|
|
||||||
docShareId: route.query.docShareId as string,
|
|
||||||
password: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const validatePassword = (value: string | undefined, callback: (error?: string) => void) => {
|
|
||||||
const sixDigitRegex = /^\d{6}$/;
|
|
||||||
|
|
||||||
if (value === undefined || value === '') {
|
|
||||||
callback(t('apiTestManagement.enterPassword'));
|
|
||||||
} else if (!sixDigitRegex.test(value)) {
|
|
||||||
callback(t('apiTestManagement.enterPassword'));
|
|
||||||
} else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const rules = {
|
|
||||||
password: [
|
|
||||||
{
|
|
||||||
required: true,
|
|
||||||
message: t('apiTestManagement.sharePasswordPlaceholder'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
validator: validatePassword,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// 上一条|下一条
|
|
||||||
function toggleDetail(type: string) {
|
|
||||||
if (type === 'prev') {
|
|
||||||
moduleTreeRef.value?.previousApi();
|
|
||||||
} else {
|
|
||||||
moduleTreeRef.value?.nextApi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const formRef = ref<FormInstance>();
|
|
||||||
|
|
||||||
// 关闭分享
|
|
||||||
function closeShareHandler() {
|
|
||||||
checkPsdModal.value = false;
|
|
||||||
formRef.value?.resetFields();
|
|
||||||
checkForm.value.password = '';
|
|
||||||
}
|
|
||||||
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 {
|
|
||||||
shareDetailInfo.value = await shareDetail(docShareId.value);
|
|
||||||
// 资源无效
|
|
||||||
if (shareDetailInfo.value.invalid) {
|
|
||||||
router.push({
|
|
||||||
name: NOT_FOUND_RESOURCE,
|
|
||||||
query: {
|
|
||||||
type: 'EXPIRED',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 限制访问校验
|
|
||||||
if (shareDetailInfo.value.isPrivate && !docCheckStore.isDocVerified(docShareId.value, userStore.id || '')) {
|
|
||||||
checkPsdModal.value = true;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const previousNode = ref<ModuleTreeNode | null>();
|
|
||||||
const nextNode = ref<ModuleTreeNode | null>();
|
|
||||||
|
|
||||||
// 设置当前预览节点
|
|
||||||
function openCurrentNode(node: ModuleTreeNode, apiNodes: ModuleTreeNode[]) {
|
|
||||||
const index = apiNodes.indexOf(node);
|
|
||||||
currentNode.value = node;
|
|
||||||
previousNode.value = index > 0 ? apiNodes[index - 1] : null;
|
|
||||||
nextNode.value = index < apiNodes.length - 1 ? apiNodes[index + 1] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 校验密码
|
|
||||||
function handleCheckPsd() {
|
|
||||||
formRef.value?.validate(async (errors) => {
|
|
||||||
if (!errors) {
|
|
||||||
try {
|
|
||||||
checkLoading.value = true;
|
|
||||||
const res = await checkSharePsd(checkForm.value);
|
|
||||||
if (res) {
|
|
||||||
closeShareHandler();
|
|
||||||
// 标记为已验证
|
|
||||||
docCheckStore.markDocAsVerified(docShareId.value, userStore.id || '');
|
|
||||||
checkPsdModal.value = false;
|
|
||||||
} else {
|
|
||||||
Message.error(t('apiTestManagement.apiSharePsdError'));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.log(error);
|
|
||||||
} finally {
|
|
||||||
checkLoading.value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const taskDrawerVisible = ref(false);
|
const taskDrawerVisible = ref(false);
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
if (docShareId.value) {
|
|
||||||
getShareDetail();
|
|
||||||
}
|
|
||||||
if (route.query.taskDrawer) {
|
if (route.query.taskDrawer) {
|
||||||
taskDrawerVisible.value = true;
|
taskDrawerVisible.value = true;
|
||||||
}
|
}
|
||||||
|
@ -441,8 +214,6 @@
|
||||||
provide('refreshModuleTree', refreshModuleTree);
|
provide('refreshModuleTree', refreshModuleTree);
|
||||||
provide('refreshModuleTreeCount', refreshModuleTreeCount);
|
provide('refreshModuleTreeCount', refreshModuleTreeCount);
|
||||||
provide('folderTreePathMap', folderTreePathMap.value);
|
provide('folderTreePathMap', folderTreePathMap.value);
|
||||||
provide('docShareId', docShareId.value);
|
|
||||||
provide('shareDetailInfo', shareDetailInfo);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
@ -498,21 +269,4 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.no-resource-svg {
|
|
||||||
margin: 0 auto 24px;
|
|
||||||
width: 160px;
|
|
||||||
height: 98px;
|
|
||||||
background: url('@/assets/svg/no_resource.svg');
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
:deep(.ms-modal-share) {
|
|
||||||
.arco-modal-mask {
|
|
||||||
background: var(--color-text-1) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
:deep(.password-form) {
|
|
||||||
.arco-form-item-message {
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in New Issue