feat(文件管理): 文件管理部分接口&部分组件调整

This commit is contained in:
baiqi 2023-11-13 18:00:30 +08:00 committed by 刘瑞斌
parent 9e5285faa1
commit f80334261d
11 changed files with 217 additions and 80 deletions

View File

@ -1,6 +1,7 @@
import MSR from '@/api/http/index';
import {
AddModuleUrl,
AddRepositoryFileUrl,
AddRepositoryUrl,
BatchDownloadFileUrl,
BatchMoveFileUrl,
@ -15,11 +16,13 @@ import {
GetModuleUrl,
GetRepositoryFileTypesUrl,
GetRepositoryFileUrl,
GetRepositoryInfoUrl,
MoveModuleUrl,
ReuploadFileUrl,
ToggleJarFileUrl,
UpdateFileUrl,
UpdateModuleUrl,
UpdateRepositoryFileUrl,
UpdateRepositoryUrl,
UploadFileUrl,
} from '@/api/requrls/project-management/fileManagement';
@ -27,6 +30,7 @@ import {
import type { CommonList } from '@/models/common';
import type {
AddModuleParams,
AddRepositoryFileParams,
AddRepositoryParams,
BatchFileApiParams,
FileDetail,
@ -36,6 +40,7 @@ import type {
ModuleTreeNode,
MoveModuleParams,
Repository,
RepositoryInfo,
ReuploadFileParams,
TestRepositoryConnectParams,
UpdateFileParams,
@ -153,3 +158,18 @@ export function connectRepository(data: TestRepositoryConnectParams) {
export function updateRepository(data: UpdateRepositoryParams) {
return MSR.post({ url: UpdateRepositoryUrl, data });
}
// 添加存储库文件
export function addRepositoryFile(data: AddRepositoryFileParams) {
return MSR.post({ url: AddRepositoryFileUrl, data });
}
// 更新存储库文件
export function updateRepositoryFile(id: string) {
return MSR.get({ url: UpdateRepositoryFileUrl, params: id });
}
// 获取存储库信息
export function getRepositoryInfo(id: string) {
return MSR.get<RepositoryInfo>({ url: GetRepositoryInfoUrl, params: id });
}

View File

@ -22,3 +22,6 @@ export const GetRepositoryFileTypesUrl = '/project/file/repository/file-type'; /
export const UpdateRepositoryUrl = '/project/file/repository/update-repository'; // 修改存储库信息
export const ConnectRepositoryUrl = '/project/file/repository/connect'; // 测试存储库连接
export const AddRepositoryUrl = '/project/file/repository/add-repository'; // 添加存储库
export const AddRepositoryFileUrl = '/project/file/repository/add-file'; // 添加存储库文件
export const UpdateRepositoryFileUrl = '/project/file/repository/pull-file'; // 更新存储库文件
export const GetRepositoryInfoUrl = '/project/file/repository/info'; // 获取存储库信息

View File

@ -139,8 +139,7 @@
const listRef: Ref = ref(null);
const { isArrivedBottom, isInitListener, containerStatusClass, setContainer, initScrollListener } =
useContainerShadow({
const { isInitListener, containerStatusClass, setContainer, initScrollListener } = useContainerShadow({
overHeight: props.itemHeight,
containerClassName: 'ms-list',
});

View File

@ -11,14 +11,16 @@
</div>
</template>
<template #second>
<div class="ms-split-box ms-split-box--right">
<div class="expand-icon" @click="changeFolderExpand">
<div class="absolute flex h-full w-[16px] items-center">
<div class="expand-icon" @click="changeLeftExpand">
<MsIcon
:type="isExpandedLeft ? 'icon-icon_up-left_outlined' : 'icon-icon_down-right_outlined'"
class="text-[var(--color-text-brand)]"
size="12"
/>
</div>
</div>
<div class="ms-split-box ms-split-box--right">
<slot name="right"></slot>
</div>
</template>
@ -66,7 +68,7 @@
const isExpandedLeft = ref(true);
const isExpandAnimating = ref(false); //
function changeFolderExpand() {
function changeLeftExpand() {
isExpandAnimating.value = true;
isExpandedLeft.value = !isExpandedLeft.value;
if (isExpandedLeft.value) {
@ -90,12 +92,16 @@
@apply hidden;
}
:deep(.arco-split-pane) {
overflow: hidden;
@apply relative overflow-hidden;
}
}
:deep(.arco-split-pane) {
@apply relative overflow-hidden;
}
.animating {
:deep(.arco-split-pane) {
overflow: hidden;
@apply relative overflow-hidden;
transition: flex 0.3s ease;
}
}
@ -106,17 +112,15 @@
.ms-split-box--left {
width: calc(v-bind(innerWidth) - 4px);
}
.ms-split-box--right {
@apply w-full;
.expand-icon {
@apply absolute cursor-pointer;
@apply z-10 cursor-pointer;
top: 50%;
transform: translateY(-50%);
padding: 8px 2px;
border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0;
background-color: var(--color-text-n8);
}
.ms-split-box--right {
@apply w-full;
}
:deep(.arco-split-trigger-icon) {
font-size: 14px;

View File

@ -111,12 +111,25 @@ export interface AddRepositoryParams extends RepositoryCommon {
url: string;
}
// 更新存储库信息入参
export interface UpdateRepositoryParams extends RepositoryCommon {
export interface UpdateRepositoryParams extends Partial<RepositoryCommon> {
id: string;
}
// 存储库信息
export interface RepositoryInfo extends RepositoryCommon {
id: string;
url: string;
projectId: string;
}
// 测试存储库连接
export interface TestRepositoryConnectParams {
url: string;
token: string;
userName: string;
}
// 添加存储库文件入参
export interface AddRepositoryFileParams {
moduleId: string;
branch: string;
filePath: string;
enable: boolean;
}

View File

@ -34,11 +34,12 @@
{{ t('project.fileManagement.download') }}
</MsButton>
<MsButton
v-if="detail?.storage !== 'minio'"
v-if="detail?.storage === 'git'"
type="icon"
status="secondary"
class="!rounded-[var(--border-radius-small)] !text-[var(--color-text-1)]"
:disabled="loading"
@click="upgradeRepositoryFile"
>
<MsIcon type="icon-icon_reset_outlined" class="mr-[4px]" />
{{ t('project.fileManagement.updateFile') }}
@ -58,7 +59,7 @@
</a-skeleton>
<template v-else>
<div class="mb-[16px] h-[102px] w-[102px]">
<a-spin class="h-full w-full" :loading="replaceLoading">
<a-spin class="h-full w-full" :loading="fileLoading">
<MsThumbnailCard
mode="hover"
:type="detail?.fileType"
@ -205,12 +206,13 @@
reuploadFile,
toggleJarFileStatus,
updateFile,
updateRepositoryFile,
} from '@/api/modules/project-management/fileManagement';
import { CompressImgUrl, OriginImgUrl } from '@/api/requrls/project-management/fileManagement';
import { useI18n } from '@/hooks/useI18n';
import useLocale from '@/locale/useLocale';
import useUserStore from '@/store/modules/user';
import { downloadByteFile, downloadUrlFile, formatFileSize } from '@/utils';
import { downloadByteFile, formatFileSize } from '@/utils';
import { FileDetail } from '@/models/projectManagement/file';
import { TableKeyEnum } from '@/enums/tableEnum';
@ -349,13 +351,13 @@
}
}
const replaceLoading = ref(false);
const fileLoading = ref(false);
watch(
() => newFile.value,
async (data) => {
if (data) {
try {
replaceLoading.value = true;
fileLoading.value = true;
await reuploadFile({
request: {
fileId: props.fileId,
@ -369,7 +371,7 @@
// eslint-disable-next-line no-console
console.log(error);
} finally {
replaceLoading.value = false;
fileLoading.value = false;
}
}
}
@ -400,6 +402,23 @@
});
}
/**
* 更新仓库文件
*/
async function upgradeRepositoryFile() {
try {
fileLoading.value = true;
await updateRepositoryFile(props.fileId);
Message.success(t('common.updateSuccess'));
detailDrawerRef.value?.initDetail();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
fileLoading.value = false;
}
}
const activeTab = ref('case');
// const caseColumns: MsTableColumn = [

View File

@ -51,7 +51,12 @@
import { ref, watch } from 'vue';
import { Message } from '@arco-design/web-vue';
import { addModule, updateFile, updateModule } from '@/api/modules/project-management/fileManagement';
import {
addModule,
updateFile,
updateModule,
updateRepository,
} from '@/api/modules/project-management/fileManagement';
import { useI18n } from '@/hooks/useI18n';
import useAppStore from '@/store/modules/app';
@ -66,7 +71,7 @@
}
const props = defineProps<{
mode: 'add' | 'rename' | 'fileRename' | 'fileUpdateDesc';
mode: 'add' | 'rename' | 'fileRename' | 'fileUpdateDesc' | 'repositoryRename';
visible?: boolean;
title?: string;
allNames: string[];
@ -153,6 +158,14 @@
});
Message.success(t('project.fileManagement.updateDescSuccess'));
emit('updateDescFinish', form.value.field);
} else if (props.mode === 'repositoryRename') {
//
await updateRepository({
id: props.nodeId || '',
name: form.value.field,
});
Message.success(t('project.fileManagement.renameSuccess'));
emit('renameFinish', form.value.field);
}
if (done) {
done(true);

View File

@ -186,6 +186,20 @@
@close="handleStorageModalCancel"
>
<a-form ref="storageFormRef" class="rounded-[4px]" :model="storageForm" layout="vertical">
<a-form-item
v-if="isMyOrAllFolder"
field="storage"
:label="t('project.fileManagement.storage')"
:rules="[{ required: true, message: t('project.fileManagement.storageNotNull') }]"
required
asterisk-position="end"
>
<a-select
v-model:model-value="storageForm.storage"
:placeholder="t('project.fileManagement.storagePlaceholder')"
:options="storageList.map((e) => ({ ...e, label: e.name, value: e.id }))"
/>
</a-form-item>
<a-form-item
field="branch"
:label="t('project.fileManagement.gitBranch')"
@ -292,12 +306,14 @@
import FolderTree from './folderTree.vue';
import {
addRepositoryFile,
batchDownloadFile,
batchMoveFile,
deleteFile,
downloadFile,
getFileList,
getFileTypes,
getRepositories,
getRepositoryFileTypes,
toggleJarFileStatus,
uploadFile,
@ -311,7 +327,7 @@
import useUserStore from '@/store/modules/user';
import { characterLimit, downloadByteFile, formatFileSize } from '@/utils';
import type { FileItem, FileListQueryParams } from '@/models/projectManagement/file';
import type { FileItem, FileListQueryParams, Repository } from '@/models/projectManagement/file';
import { RouteEnum } from '@/enums/routeEnum';
import { TableKeyEnum } from '@/enums/tableEnum';
import { UploadStatus } from '@/enums/uploadEnum';
@ -387,7 +403,17 @@
return 'file-type-card';
}
const normalFileActions: ActionsItem[] = [
const normalFileActions = computed(() => {
if (fileType.value === 'storage') {
return [
{
label: 'project.fileManagement.delete',
eventTag: 'delete',
danger: true,
},
];
}
return [
{
label: 'project.fileManagement.move',
eventTag: 'move',
@ -401,6 +427,7 @@
danger: true,
},
];
});
function getJarFileActions(record: FileItem) {
const jarFileActions: ActionsItem[] = [
@ -536,6 +563,7 @@
currentSelectCount: 0,
});
const combine = ref<Record<string, any>>({});
const isMyOrAllFolder = computed(() => ['my', 'all'].includes(props.activeFolder)); // /
/**
* 处理表格选中
@ -554,7 +582,7 @@
selectIds: batchParams.value?.selectedIds || [],
selectAll: !!batchParams.value?.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
condition: { keyword: keyword.value, comebine: combine.value },
condition: { keyword: keyword.value, combine: combine.value },
projectId: appStore.currentProjectId,
fileType: tableFileType.value,
moduleIds: [props.activeFolder],
@ -573,11 +601,11 @@
emit('init', {
keyword: keyword.value,
fileType: tableFileType.value,
moduleIds: ['all', 'my'].includes(props.activeFolder) ? [] : [props.activeFolder],
moduleIds: isMyOrAllFolder.value ? [] : [props.activeFolder],
projectId: appStore.currentProjectId,
current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize,
comebine:
combine:
props.activeFolder === 'my'
? {
createUser: userStore.id,
@ -616,7 +644,7 @@
selectIds,
selectAll: !!batchParams.value?.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
condition: { keyword: keyword.value, comebine: combine.value },
condition: { keyword: keyword.value, combine: combine.value },
projectId: appStore.currentProjectId,
fileType: tableFileType.value,
moduleIds: [props.activeFolder],
@ -684,7 +712,7 @@
selectIds: isBatchMove.value ? batchParams.value?.selectedIds || [] : [activeFile.value?.id || ''],
selectAll: !!batchParams.value?.selectAll,
excludeIds: batchParams.value?.excludeIds || [],
condition: { keyword: keyword.value, comebine: combine.value },
condition: { keyword: keyword.value, combine: combine.value },
projectId: appStore.currentProjectId,
fileType: tableFileType.value,
moduleIds: [props.activeFolder],
@ -726,10 +754,10 @@
if (fileType.value === 'storage') {
combine.value.storage = 'git';
} else {
combine.value.storage = 'module';
combine.value.storage = 'minio';
}
let moduleIds: string[] = [props.activeFolder, ...props.offspringIds];
if (['all', 'my'].includes(props.activeFolder)) {
if (isMyOrAllFolder.value) {
moduleIds = [];
}
setLoadListParams({
@ -737,7 +765,7 @@
fileType: tableFileType.value,
moduleIds,
projectId: appStore.currentProjectId,
comebine: combine.value,
combine: combine.value,
});
}
@ -1025,11 +1053,27 @@
const storageDialogVisible = ref(false); // -
const storageForm = ref({
storage: '',
branch: '',
path: '',
}); // -
const storageFormRef = ref<FormInstance>(); // -ref
const storageModalLoading = ref(false); // -loading
const storageList = ref<Repository[]>([]); //
const storageListLoading = ref(false); // loading
async function initStorageList() {
try {
storageListLoading.value = true;
const res = await getRepositories(appStore.currentProjectId);
storageList.value = res;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
storageListLoading.value = false;
}
}
/**
* 处理添加文件按钮点击事件根据当前查看的文件类型打开不同的弹窗
@ -1039,6 +1083,10 @@
uploadDrawerVisible.value = true;
} else if (fileType.value === 'storage') {
storageDialogVisible.value = true;
if (isMyOrAllFolder.value) {
// /id
initStorageList();
}
}
}
@ -1053,10 +1101,12 @@
*/
async function addStorageFile(isContinue?: boolean) {
const params = {
moduleId: isMyOrAllFolder.value ? storageForm.value.storage : props.activeFolder,
branch: storageForm.value.branch,
path: storageForm.value.path,
filePath: storageForm.value.path,
enable: false,
};
// await batchCreateUser(params);
await addRepositoryFile(params);
Message.success(t('common.addSuccess'));
if (!isContinue) {
storageDialogVisible.value = false;

View File

@ -9,35 +9,37 @@
<MsList
v-model:focus-item-key="focusItemKey"
:virtual-list-props="{
height: 'calc(100vh - 310px)',
height: 'calc(100vh - 325px)',
}"
:data="storageList"
:bordered="false"
:split="false"
:item-more-actions="folderMoreActions"
:empty-text="t('project.fileManagement.noStorage')"
item-key-field="id"
class="mr-[-6px]"
@more-action-select="handleMoreSelect"
@more-actions-close="moreActionsClose"
>
<template #title="{ item, index }">
<div :key="index" class="storage" @click="setActiveFolder(item.key)">
<div :class="props.activeFolder === item.key ? 'storage-text storage-text--active' : 'storage-text'">
<div :key="index" class="storage" @click="setActiveFolder(item.id)">
<div :class="props.activeFolder === item.id ? 'storage-text storage-text--active' : 'storage-text'">
<MsIcon type="icon-icon_git" class="storage-icon" />
<div class="storage-name">{{ item.title }}</div>
<div class="storage-name">{{ item.name }}</div>
<div class="storage-count">({{ item.count }})</div>
</div>
</div>
</template>
<template #itemAction="{ item }">
<popConfirm
mode="rename"
:parent-id="item.key"
mode="repositoryRename"
:node-id="item.id"
:field-config="{ field: renameStorageTitle }"
:all-names="[]"
@close="resetFocusItemKey"
@rename-finish="(val) => (item.name = val)"
>
<span :id="`renameSpan${item.key}`" class="relative"></span>
<span :id="`renameSpan${item.id}`" class="relative"></span>
</popConfirm>
</template>
</MsList>
@ -146,13 +148,19 @@
import MsFormItemSub from '@/components/business/ms-form-item-sub/index.vue';
import popConfirm from './popConfirm.vue';
import { addRepository, connectRepository, getRepositories } from '@/api/modules/project-management/fileManagement';
import {
addRepository,
connectRepository,
getRepositories,
getRepositoryInfo,
updateRepository,
} from '@/api/modules/project-management/fileManagement';
import { useI18n } from '@/hooks/useI18n';
import useModal from '@/hooks/useModal';
import useAppStore from '@/store/modules/app';
import { validateGitUrl } from '@/utils/validate';
import { Repository } from '@/models/projectManagement/file';
import { Repository, RepositoryInfo } from '@/models/projectManagement/file';
import { GitPlatformEnum } from '@/enums/commonEnum';
const props = defineProps<{
@ -209,6 +217,7 @@
loading.value = true;
const res = await getRepositories(appStore.currentProjectId);
originStorageList.value = res;
storageList.value = [...originStorageList.value];
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -241,7 +250,7 @@
function deleteStorage(item: any) {
openModal({
type: 'error',
title: t('project.fileManagement.deleteStorageTipTitle', { name: item.title }),
title: t('project.fileManagement.deleteStorageTipTitle', { name: item.name }),
content: t('project.fileManagement.deleteStorageTipContent'),
okText: t('project.fileManagement.deleteConfirm'),
okButtonProps: {
@ -292,7 +301,9 @@
const drawerLoading = ref(false);
const isEdit = ref(false);
const activeStorageForm = ref({
const activeStorageForm = ref<RepositoryInfo>({
id: '',
projectId: '',
name: '',
platform: GitPlatformEnum.GITHUB,
url: '',
@ -305,13 +316,8 @@
async function getStorageDetail(id: string) {
try {
drawerLoading.value = true;
activeStorageForm.value = {
name: 'xxx',
platform: GitPlatformEnum.GITHUB,
url: 'xxxxxxx',
token: 'sxsxsx',
userName: 'ddwdwdwwd',
};
const res = await getRepositoryInfo(id);
activeStorageForm.value = res;
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
@ -325,21 +331,21 @@
* @param item
* @param listItem
*/
function handleMoreSelect(item: ActionsItem, listItem: any) {
function handleMoreSelect(item: ActionsItem, listItem: Repository) {
switch (item.eventTag) {
case 'delete':
deleteStorage(listItem);
resetFocusItemKey();
break;
case 'rename':
renameStorageTitle.value = listItem.title || '';
renameStorageTitle.value = listItem.name || '';
renamePopVisible.value = true;
document.querySelector(`#renameSpan${listItem.key}`)?.dispatchEvent(new Event('click'));
document.querySelector(`#renameSpan${listItem.id}`)?.dispatchEvent(new Event('click'));
break;
case 'edit':
isEdit.value = true;
showDrawer.value = true;
getStorageDetail(listItem.key);
getStorageDetail(listItem.id);
break;
default:
break;
@ -382,14 +388,20 @@
async function saveStorage(isContinue: boolean) {
try {
drawerLoading.value = true;
if (isEdit.value) {
await updateRepository({
...activeStorageForm.value,
});
} else {
await addRepository({
...activeStorageForm.value,
projectId: appStore.currentProjectId,
});
}
if (!isContinue) {
handleDrawerCancel();
}
Message.success(t('common.addSuccess'));
Message.success(isEdit.value ? t('common.updateSuccess') : t('common.addSuccess'));
initRepositories();
} catch (error) {
// eslint-disable-next-line no-console

View File

@ -113,6 +113,8 @@ export default {
'project.fileManagement.record': 'Update history',
'project.fileManagement.caseList': 'Use case list',
'project.fileManagement.search': 'Enter name to search',
'project.fileManagement.storagePlaceholder': 'Please select a repository',
'project.fileManagement.storageNotNull': 'Repository cannot be empty',
'project.fileManagement.gitBranchNotNull': 'Git branch cannot be empty',
'project.fileManagement.gitBranchPlaceholder': 'Please enter the git branch, such as: master',
'project.fileManagement.gitFilePath': 'File path',

View File

@ -105,6 +105,8 @@ export default {
'project.fileManagement.record': '更新历史',
'project.fileManagement.caseList': '用例列表',
'project.fileManagement.search': '输入名称搜索',
'project.fileManagement.storagePlaceholder': '请选择存储库',
'project.fileManagement.storageNotNull': '存储库不能为空',
'project.fileManagement.gitBranchNotNull': 'git 分支不能为空',
'project.fileManagement.gitBranchPlaceholder': '请输入 git 分支master',
'project.fileManagement.gitFilePath': '文件路径',