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

This commit is contained in:
baiqi 2023-11-20 18:23:34 +08:00 committed by Craftsman
parent 58fb5d7357
commit 4bf12605ad
10 changed files with 149 additions and 90 deletions

View File

@ -12,6 +12,7 @@ import {
FilePageUrl, FilePageUrl,
GetAssociationListUrl, GetAssociationListUrl,
GetFileDetailUrl, GetFileDetailUrl,
GetFileHistoryListUrl,
GetFileTypesUrl, GetFileTypesUrl,
GetModuleCountUrl, GetModuleCountUrl,
GetModuleUrl, GetModuleUrl,
@ -37,6 +38,7 @@ import type {
AssociationItem, AssociationItem,
BatchFileApiParams, BatchFileApiParams,
FileDetail, FileDetail,
FileHistoryItem,
FileItem, FileItem,
FileListQueryParams, FileListQueryParams,
ModuleCount, ModuleCount,
@ -127,6 +129,11 @@ export function getFileDetail(id: string) {
return MSR.get<FileDetail>({ url: GetFileDetailUrl, params: id }); return MSR.get<FileDetail>({ url: GetFileDetailUrl, params: id });
} }
// 获取文件历史版本列表
export function getFileHistoryList(params: { id: string }) {
return MSR.get<FileHistoryItem[]>({ url: GetFileHistoryListUrl, params: params.id });
}
// jar文件启用禁用 // jar文件启用禁用
export function toggleJarFileStatus(id: string, status: boolean) { export function toggleJarFileStatus(id: string, status: boolean) {
return MSR.get({ url: `${ToggleJarFileUrl}/${id}/${status}` }); return MSR.get({ url: `${ToggleJarFileUrl}/${id}/${status}` });

View File

@ -28,3 +28,4 @@ export const GetRepositoryInfoUrl = '/project/file/repository/info'; // 获取
export const DeleteAssociationUrl = '/project/file/association/delete'; // 删除关联用例 export const DeleteAssociationUrl = '/project/file/association/delete'; // 删除关联用例
export const UpgradeAssociationUrl = '/project/file/association/upgrade'; // 更新关联用例文件 export const UpgradeAssociationUrl = '/project/file/association/upgrade'; // 更新关联用例文件
export const GetAssociationListUrl = '/project/file/association/list'; // 获取关联用例列表 export const GetAssociationListUrl = '/project/file/association/list'; // 获取关联用例列表
export const GetFileHistoryListUrl = '/project/file/file-version'; // 获取文件历史版本列表

View File

@ -8,6 +8,7 @@
props.isFullscreen ? 'ms-card--no-radius' : '', props.isFullscreen ? 'ms-card--no-radius' : '',
props.autoHeight ? '' : 'min-h-[500px]', props.autoHeight ? '' : 'min-h-[500px]',
props.noContentPadding ? 'ms-card--noContentPadding' : 'p-[24px]', props.noContentPadding ? 'ms-card--noContentPadding' : 'p-[24px]',
props.noBottomRadius ? 'ms-card--noBottomRadius' : '',
]" ]"
> >
<div v-if="!props.simple" class="card-header"> <div v-if="!props.simple" class="card-header">
@ -20,7 +21,7 @@
</div> </div>
<a-divider v-if="!props.simple" class="mb-[16px]" /> <a-divider v-if="!props.simple" class="mb-[16px]" />
<div class="ms-card-container"> <div class="ms-card-container">
<a-scrollbar class="pr-[5px]" :style="getComputedContentStyle"> <a-scrollbar :class="props.noContentPadding ? '' : 'pr-[5px]'" :style="getComputedContentStyle">
<div class="relative h-full w-full" :style="{ minWidth: `${props.minWidth || 1000}px` }"> <div class="relative h-full w-full" :style="{ minWidth: `${props.minWidth || 1000}px` }">
<slot></slot> <slot></slot>
</div> </div>
@ -74,6 +75,7 @@
minWidth: number; // minWidth: number; //
hasBreadcrumb: boolean; // hasBreadcrumb: boolean; //
noContentPadding: boolean; // padding noContentPadding: boolean; // padding
noBottomRadius?: boolean; //
isFullscreen?: boolean; // isFullscreen?: boolean; //
handleBack: () => void; // handleBack: () => void; //
}> }>
@ -88,6 +90,7 @@
autoHeight: false, autoHeight: false,
hasBreadcrumb: false, hasBreadcrumb: false,
noContentPadding: false, noContentPadding: false,
noBottomRadius: false,
} }
); );
@ -107,7 +110,7 @@
const cardOverHeight = computed(() => { const cardOverHeight = computed(() => {
if (props.simple) { if (props.simple) {
// //
return 136 + _specialHeight; return props.noContentPadding ? 88 : 136 + _specialHeight;
} }
if (props.hideFooter) { if (props.hideFooter) {
// //
@ -117,6 +120,13 @@
}); });
const getComputedContentStyle = computed(() => { const getComputedContentStyle = computed(() => {
if (props.noContentPadding) {
return {
overflow: 'auto',
width: 'auto',
height: props.autoHeight ? 'auto' : `calc(100vh - ${cardOverHeight.value}px)`,
};
}
if (props.isFullscreen) { if (props.isFullscreen) {
return { return {
overflow: 'auto', overflow: 'auto',
@ -149,7 +159,7 @@
border-radius: var(--border-radius-large); border-radius: var(--border-radius-large);
box-shadow: 0 0 10px rgb(120 56 135 / 5%); box-shadow: 0 0 10px rgb(120 56 135 / 5%);
&--noContentPadding { &--noContentPadding {
border-radius: var(--border-radius-large) var(--border-radius-large) 0 0; border-radius: var(--border-radius-large);
.card-header { .card-header {
padding: 24px 24px 0; padding: 24px 24px 0;
} }
@ -157,6 +167,9 @@
@apply mb-0; @apply mb-0;
} }
} }
&--noBottomRadius {
border-radius: var(--border-radius-large) var(--border-radius-large) 0 0;
}
.card-header { .card-header {
@apply flex items-center; @apply flex items-center;
.back-btn { .back-btn {

View File

@ -1,5 +1,5 @@
<template> <template>
<MsCard class="mb-[16px]" :title="props.title" hide-back hide-footer auto-height no-content-padding> <MsCard class="mb-[16px]" :title="props.title" hide-back hide-footer auto-height no-content-padding no-bottom-radius>
<a-tabs v-model:active-key="innerTab" class="no-content"> <a-tabs v-model:active-key="innerTab" class="no-content">
<a-tab-pane v-for="item of tabList" :key="item.key" :title="item.title" /> <a-tab-pane v-for="item of tabList" :key="item.key" :title="item.title" />
</a-tabs> </a-tabs>

View File

@ -22,6 +22,9 @@ export interface FileItem {
previewSrc: string; // 预览地址 previewSrc: string; // 预览地址
size: number; size: number;
enable: boolean; // jar文件启用禁用 enable: boolean; // jar文件启用禁用
branch?: string; // 分支
filePath?: string; // 文件路径
fileVersion?: string; // 文件版本
} }
// 文件详情 // 文件详情
export interface FileDetail extends FileItem { export interface FileDetail extends FileItem {
@ -88,6 +91,14 @@ export interface ModuleTreeNode {
type: string; type: string;
children: ModuleTreeNode[]; children: ModuleTreeNode[];
} }
// 文件历史列表项
export interface FileHistoryItem {
id: string;
fileVersion: string;
updateHistory: string;
operator: string;
operateTime: number;
}
// 存储库列表 // 存储库列表
export interface Repository { export interface Repository {
id: string; id: string;

View File

@ -46,7 +46,7 @@
</MsButton> </MsButton>
</template> </template>
<template #default="{ loading, detail }"> <template #default="{ loading, detail }">
<div class="flex h-full"> <div class="flex h-full w-full">
<div class="file-detail"> <div class="file-detail">
<a-skeleton v-if="loading" :loading="loading" :animation="true"> <a-skeleton v-if="loading" :loading="loading" :animation="true">
<a-skeleton-shape size="large" class="mb-[16px] h-[102px] w-[102px]" /> <a-skeleton-shape size="large" class="mb-[16px] h-[102px] w-[102px]" />
@ -149,33 +149,33 @@
:placeholder="t('project.fileManagement.search')" :placeholder="t('project.fileManagement.search')"
allow-clear allow-clear
class="w-[240px]" class="w-[240px]"
@press-enter="searchCase"
@search="searchCase"
> >
</a-input-search> </a-input-search>
</div> </div>
<div class="p-[16px]"> <div class="p-[16px]">
<a-spin class="w-full" :loading="caseLoading">
<ms-base-table
v-if="activeTab === 'case'"
v-bind="caseTableProps"
:data="caseList"
no-disable
:action-config="caseBatchActions"
v-on="caseTableEvent"
>
<template #action="{ record }">
<MsButton type="text" class="mr-[8px]" @click="updateCase(record)">
{{ t('project.fileManagement.updateCaseFile') }}
</MsButton>
</template>
</ms-base-table>
</a-spin>
<ms-base-table <ms-base-table
v-if="activeTab === 'case'"
v-bind="caseTableProps"
:data="caseList"
no-disable
:action-config="caseBatchActions"
v-on="caseTableEvent"
>
<template #action="{ record }">
<MsButton type="text" class="mr-[8px]" @click="updateCase(record)">
{{ t('project.fileManagement.updateCaseFile') }}
</MsButton>
</template>
</ms-base-table>
<!-- <ms-base-table
v-if="activeTab === 'version'" v-if="activeTab === 'version'"
v-bind="versionTableProps" v-bind="versionTableProps"
no-disable no-disable
v-on="versionTableEvent" v-on="versionTableEvent"
> >
</ms-base-table> --> </ms-base-table>
</div> </div>
</div> </div>
</div> </div>
@ -185,7 +185,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { useFileSystemAccess } from '@vueuse/core'; import { useFileSystemAccess } from '@vueuse/core';
import { Message } from '@arco-design/web-vue'; import { Message } from '@arco-design/web-vue';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
@ -205,18 +205,21 @@
downloadFile, downloadFile,
getAssociationList, getAssociationList,
getFileDetail, getFileDetail,
getFileHistoryList,
reuploadFile, reuploadFile,
toggleJarFileStatus, toggleJarFileStatus,
updateFile, updateFile,
updateRepositoryFile, updateRepositoryFile,
upgradeAssociation,
} from '@/api/modules/project-management/fileManagement'; } from '@/api/modules/project-management/fileManagement';
import { CompressImgUrl, OriginImgUrl } from '@/api/requrls/project-management/fileManagement'; import { CompressImgUrl, OriginImgUrl } from '@/api/requrls/project-management/fileManagement';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
import useLocale from '@/locale/useLocale'; import useLocale from '@/locale/useLocale';
import { useAppStore } from '@/store';
import useUserStore from '@/store/modules/user'; import useUserStore from '@/store/modules/user';
import { downloadByteFile, formatFileSize } from '@/utils'; import { downloadByteFile, formatFileSize } from '@/utils';
import { FileDetail } from '@/models/projectManagement/file'; import { AssociationItem, FileDetail } from '@/models/projectManagement/file';
const props = defineProps<{ const props = defineProps<{
visible: boolean; visible: boolean;
@ -233,6 +236,7 @@
const { t } = useI18n(); const { t } = useI18n();
const { currentLocale } = useLocale(); const { currentLocale } = useLocale();
const userStore = useUserStore(); const userStore = useUserStore();
const appStore = useAppStore();
const innerVisible = ref(false); const innerVisible = ref(false);
const fileDescriptions = ref<Description[]>([]); const fileDescriptions = ref<Description[]>([]);
@ -335,18 +339,18 @@
3, 3,
0, 0,
...[ ...[
// { {
// label: t('project.fileManagement.gitBranch'), label: t('project.fileManagement.gitBranch'),
// value: detail.gitBranch, value: detail.branch || '',
// }, },
// { {
// label: t('project.fileManagement.gitPath'), label: t('project.fileManagement.gitPath'),
// value: detail.gitPath, value: detail.filePath || '',
// }, },
// { {
// label: t('project.fileManagement.gitVersion'), label: t('project.fileManagement.gitVersion'),
// value: detail.gitVersion, value: detail.fileVersion || '',
// }, },
] ]
); );
} }
@ -454,7 +458,6 @@
propsEvent: caseTableEvent, propsEvent: caseTableEvent,
loadList: loadCaseList, loadList: loadCaseList,
setLoadListParams, setLoadListParams,
setKeyword,
} = useTable(getAssociationList, { } = useTable(getAssociationList, {
scroll: { x: 800 }, scroll: { x: 800 },
columns: caseColumns, columns: caseColumns,
@ -474,65 +477,82 @@
}; };
const keyword = ref(''); const keyword = ref('');
const caseList = ref<any[]>([]); const caseList = computed(() => caseTableProps.value.data.filter((item) => item.sourceName.includes(keyword.value)));
const caseLoading = ref(false);
function filterCaseList() { async function updateCase(record: AssociationItem) {
caseList.value = caseTableProps.value.data.filter((item) => item.sourceName.includes(keyword.value)); try {
caseLoading.value = true;
await upgradeAssociation(appStore.currentProjectId, record.id);
Message.success(t('project.fileManagement.updateCaseFileSuccess'));
loadCaseList();
} catch (error) {
// eslint-disable-next-line no-console
console.log(error);
} finally {
caseLoading.value = false;
}
} }
async function searchCase() { const versionColumns: MsTableColumn = [
setKeyword(keyword.value); {
await loadCaseList(); title: 'project.fileManagement.fileVersion',
filterCaseList(); dataIndex: 'fileVersion',
} showTooltip: true,
width: 180,
},
{
title: 'project.fileManagement.record',
dataIndex: 'updateHistory',
showTooltip: true,
},
{
title: 'project.fileManagement.operator',
dataIndex: 'operator',
showTooltip: true,
},
{
title: 'project.fileManagement.operatorTime',
dataIndex: 'operateTime',
width: 180,
},
];
const {
propsRes: versionTableProps,
propsEvent: versionTableEvent,
loadList: loadVersionList,
setLoadListParams: setVersionLoadListParams,
} = useTable(
getFileHistoryList,
{
scroll: { x: '100%' },
columns: versionColumns,
selectable: false,
showPagination: false,
},
(item) => {
return {
...item,
operateTime: dayjs(item.operateTime).format('YYYY-MM-DD HH:mm:ss'),
};
}
);
function updateCase(record: any) { watchEffect(() => {
console.log(record);
}
// const versionColumns: MsTableColumn = [
// {
// title: 'project.fileManagement.fileVersion',
// dataIndex: 'fileVersion',
// },
// {
// title: 'project.fileManagement.record',
// dataIndex: 'record',
// showTooltip: true,
// },
// {
// title: 'project.fileManagement.creator',
// dataIndex: 'creator',
// showTooltip: true,
// },
// {
// title: 'project.fileManagement.createTime',
// dataIndex: 'createTime',
// width: 180,
// },
// ];
// const {
// propsRes: versionTableProps,
// propsEvent: versionTableEvent,
// loadList: loadVersionList,
// } = useTable(getFileVersions, {
// tableKey: TableKeyEnum.FILE_MANAGEMENT_VERSION,
// scroll: { x: '100%' },
// columns: versionColumns,
// selectable: false,
// });
watchEffect(async () => {
if (innerVisible.value) { if (innerVisible.value) {
if (activeTab.value === 'case') { if (activeTab.value === 'case') {
setLoadListParams({ setLoadListParams({
id: props.fileId, id: props.fileId,
}); });
await loadCaseList(); loadCaseList();
filterCaseList();
} else { } else {
// loadVersionList(); setVersionLoadListParams({
id: props.fileId,
});
loadVersionList();
} }
} else {
activeTab.value = 'case';
} }
}); });
</script> </script>
@ -546,7 +566,7 @@
border-right: 1px solid var(--color-text-n8); border-right: 1px solid var(--color-text-n8);
} }
.file-relation { .file-relation {
width: 660px; width: calc(100% - 300px);
} }
:deep(.no-content) { :deep(.no-content) {
.arco-tabs-tab { .arco-tabs-tab {

View File

@ -161,6 +161,7 @@
hideMoreAction: e.id === 'root', hideMoreAction: e.id === 'root',
draggable: e.id !== 'root' && !props.isModal, draggable: e.id !== 'root' && !props.isModal,
disabled: e.id === props.activeFolder && props.isModal, disabled: e.id === props.activeFolder && props.isModal,
count: props.modulesCount?.[e.id] || 0,
}; };
}); });
if (isSetDefaultKey) { if (isSetDefaultKey) {

View File

@ -587,7 +587,7 @@
condition: { keyword: keyword.value, combine: combine.value }, condition: { keyword: keyword.value, combine: combine.value },
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
fileType: tableFileType.value, fileType: tableFileType.value,
moduleIds: [props.activeFolder], moduleIds: isMyOrAllFolder.value ? [] : [props.activeFolder],
}); });
downloadByteFile(res, 'files.zip'); downloadByteFile(res, 'files.zip');
resetSelector(); resetSelector();
@ -603,7 +603,7 @@
emit('init', { emit('init', {
keyword: keyword.value, keyword: keyword.value,
fileType: tableFileType.value, fileType: tableFileType.value,
moduleIds: isMyOrAllFolder.value ? [] : [props.activeFolder], moduleIds: [],
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
current: propsRes.value.msPagination?.current, current: propsRes.value.msPagination?.current,
pageSize: propsRes.value.msPagination?.pageSize, pageSize: propsRes.value.msPagination?.pageSize,
@ -649,7 +649,7 @@
condition: { keyword: keyword.value, combine: combine.value }, condition: { keyword: keyword.value, combine: combine.value },
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
fileType: tableFileType.value, fileType: tableFileType.value,
moduleIds: [props.activeFolder], moduleIds: isMyOrAllFolder.value ? [] : [props.activeFolder],
}); });
Message.success(t('common.deleteSuccess')); Message.success(t('common.deleteSuccess'));
if (showType.value === 'card') { if (showType.value === 'card') {
@ -717,7 +717,7 @@
condition: { keyword: keyword.value, combine: combine.value }, condition: { keyword: keyword.value, combine: combine.value },
projectId: appStore.currentProjectId, projectId: appStore.currentProjectId,
fileType: tableFileType.value, fileType: tableFileType.value,
moduleIds: [props.activeFolder], moduleIds: isMyOrAllFolder.value ? [] : [props.activeFolder],
moveModuleId: selectedModuleKeys.value[0], moveModuleId: selectedModuleKeys.value[0],
}); });
Message.success(t('project.fileManagement.batchMoveSuccess')); Message.success(t('project.fileManagement.batchMoveSuccess'));

View File

@ -108,9 +108,12 @@ export default {
'project.fileManagement.cases': 'Related Use Cases', 'project.fileManagement.cases': 'Related Use Cases',
'project.fileManagement.versionHistory': 'Version history', 'project.fileManagement.versionHistory': 'Version history',
'project.fileManagement.updateCaseFile': 'Update use case file', 'project.fileManagement.updateCaseFile': 'Update use case file',
'project.fileManagement.updateCaseFileSuccess': 'Use case file updated successfully',
'project.fileManagement.id': 'ID', 'project.fileManagement.id': 'ID',
'project.fileManagement.fileVersion': 'File version', 'project.fileManagement.fileVersion': 'File version',
'project.fileManagement.record': 'Update history', 'project.fileManagement.record': 'Update history',
'project.fileManagement.operator': 'Operator',
'project.fileManagement.operatorTime': 'Operating time',
'project.fileManagement.caseList': 'Use case list', 'project.fileManagement.caseList': 'Use case list',
'project.fileManagement.search': 'Enter name to search', 'project.fileManagement.search': 'Enter name to search',
'project.fileManagement.storagePlaceholder': 'Please select a repository', 'project.fileManagement.storagePlaceholder': 'Please select a repository',

View File

@ -100,9 +100,12 @@ export default {
'project.fileManagement.cases': '关联用例', 'project.fileManagement.cases': '关联用例',
'project.fileManagement.versionHistory': '版本历史', 'project.fileManagement.versionHistory': '版本历史',
'project.fileManagement.updateCaseFile': '更新用例文件', 'project.fileManagement.updateCaseFile': '更新用例文件',
'project.fileManagement.updateCaseFileSuccess': '用例文件更新成功',
'project.fileManagement.id': 'ID', 'project.fileManagement.id': 'ID',
'project.fileManagement.fileVersion': '文件版本', 'project.fileManagement.fileVersion': '文件版本',
'project.fileManagement.record': '更新历史', 'project.fileManagement.record': '更新历史',
'project.fileManagement.operator': '操作人',
'project.fileManagement.operatorTime': '操作时间',
'project.fileManagement.caseList': '用例列表', 'project.fileManagement.caseList': '用例列表',
'project.fileManagement.search': '输入名称搜索', 'project.fileManagement.search': '输入名称搜索',
'project.fileManagement.storagePlaceholder': '请选择存储库', 'project.fileManagement.storagePlaceholder': '请选择存储库',