feat: 文件夹树形列表实现

This commit is contained in:
zhengkunwang223 2022-08-24 17:34:21 +08:00
parent ba8a13dfcc
commit cc8d6516d9
8 changed files with 121 additions and 73 deletions

View file

@ -20,3 +20,17 @@ func (b *BaseApi) ListFiles(c *gin.Context) {
} }
helper.SuccessWithData(c, files) helper.SuccessWithData(c, files)
} }
func (b *BaseApi) GetFileTree(c *gin.Context) {
var req dto.FileOption
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
tree, err := fileService.GetFileTree(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, tree)
}

View file

@ -9,3 +9,9 @@ type FileOption struct {
type FileInfo struct { type FileInfo struct {
files.FileInfo files.FileInfo
} }
type FileTree struct {
Name string `json:"name"`
Path string `json:"path"`
Children []FileTree `json:"children"`
}

View file

@ -25,3 +25,24 @@ func (f FileService) GetFileList(op dto.FileOption) (dto.FileInfo, error) {
fileInfo.FileInfo = *info fileInfo.FileInfo = *info
return fileInfo, nil return fileInfo, nil
} }
func (f FileService) GetFileTree(op dto.FileOption) ([]dto.FileTree, error) {
var treeArray []dto.FileTree
info, err := files.NewFileInfo(op.FileOption)
if err != nil {
return nil, err
}
node := dto.FileTree{
Name: info.Name,
Path: info.Path,
}
for _, v := range info.Items {
if v.IsDir {
node.Children = append(node.Children, dto.FileTree{
Name: v.Name,
Path: v.Path,
})
}
}
return append(treeArray, node), nil
}

View file

@ -16,6 +16,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi baseApi := v1.ApiGroupApp.BaseApi
{ {
fileRouter.POST("/search", baseApi.ListFiles) fileRouter.POST("/search", baseApi.ListFiles)
fileRouter.POST("/tree", baseApi.GetFileTree)
} }
} }

View file

@ -1,5 +1,5 @@
import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
import { showFullScreenLoading, tryHideFullScreenLoading } from '@/config/service-loading'; // import { showFullScreenLoading, tryHideFullScreenLoading } from '@/config/service-loading';
import { AxiosCanceler } from './helper/axios-cancel'; import { AxiosCanceler } from './helper/axios-cancel';
import { ResultData } from '@/api/interface'; import { ResultData } from '@/api/interface';
import { ResultEnum } from '@/enums/http-enum'; import { ResultEnum } from '@/enums/http-enum';
@ -31,7 +31,7 @@ class RequestHttp {
}; };
} }
axiosCanceler.addPending(config); axiosCanceler.addPending(config);
config.headers!.noLoading || showFullScreenLoading(); // config.headers!.noLoading || showFullScreenLoading();
return { return {
...config, ...config,
}; };
@ -48,7 +48,7 @@ class RequestHttp {
globalStore.setCsrfToken(response.headers['x-csrf-token']); globalStore.setCsrfToken(response.headers['x-csrf-token']);
} }
axiosCanceler.removePending(config); axiosCanceler.removePending(config);
tryHideFullScreenLoading(); // tryHideFullScreenLoading();
if (data.code == ResultEnum.OVERDUE || data.code == ResultEnum.FORBIDDEN) { if (data.code == ResultEnum.OVERDUE || data.code == ResultEnum.FORBIDDEN) {
ElMessage.error(data.msg); ElMessage.error(data.msg);
router.replace({ router.replace({
@ -64,7 +64,7 @@ class RequestHttp {
}, },
async (error: AxiosError) => { async (error: AxiosError) => {
const { response } = error; const { response } = error;
tryHideFullScreenLoading(); // tryHideFullScreenLoading();
if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时请您稍后重试'); if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时请您稍后重试');
if (response) checkStatus(response.status); if (response) checkStatus(response.status);
if (!window.navigator.onLine) router.replace({ path: '/500' }); if (!window.navigator.onLine) router.replace({ path: '/500' });

View file

@ -21,4 +21,11 @@ export namespace File {
search?: string; search?: string;
expand: boolean; expand: boolean;
} }
export interface FileTree {
name: string;
isDir: Boolean;
path: string;
children?: FileTree[];
}
} }

View file

@ -4,3 +4,7 @@ import http from '@/api';
export const GetFilesList = (params: File.ReqFile) => { export const GetFilesList = (params: File.ReqFile) => {
return http.post<File.File>('files/search', params); return http.post<File.File>('files/search', params);
}; };
export const GetFilesTree = (params: File.ReqFile) => {
return http.post<File.FileTree[]>('files/tree', params);
};

View file

@ -1,19 +1,28 @@
<template> <template>
<LayoutContent :header="$t('menu.files')"> <LayoutContent :header="$t('menu.files')">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="6"> <el-col :span="5">
<el-tree :data="dataSource" node-key="id"> <el-scrollbar height="800px">
<template #default="{ node }"> <el-tree
<el-icon v-if="node.data.isDir && node.expanded"><FolderOpened /></el-icon> :data="fileTree"
<el-icon v-if="node.data.isDir && !node.expanded"><Folder /></el-icon> :props="defaultProps"
<el-icon v-if="!node.data.isDir"><Document /></el-icon> :load="loadNode"
<span class="custom-tree-node"> lazy
<span>{{ node.data.label }}</span> node-key="id"
</span> v-loading="treeLoading"
</template> >
</el-tree> <template #default="{ node }">
<el-icon v-if="node.expanded"><FolderOpened /></el-icon>
<el-icon v-else><Folder /></el-icon>
<span class="custom-tree-node">
<span>{{ node.data.name }}</span>
</span>
</template>
</el-tree>
</el-scrollbar>
</el-col> </el-col>
<el-col :span="18">
<el-col :span="19">
<div class="path"> <div class="path">
<BreadCrumbs> <BreadCrumbs>
<BreadCrumbItem @click="jump(-1)" :right="paths.length == 0">root</BreadCrumbItem> <BreadCrumbItem @click="jump(-1)" :right="paths.length == 0">root</BreadCrumbItem>
@ -30,7 +39,7 @@
:pagination-config="paginationConfig" :pagination-config="paginationConfig"
v-model:selects="selects" v-model:selects="selects"
:data="data" :data="data"
:loading="loading" v-loading="loading"
> >
<template #toolbar> <template #toolbar>
<el-dropdown split-button type="primary"> <el-dropdown split-button type="primary">
@ -89,26 +98,29 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from '@vue/runtime-core'; import { reactive, ref } from '@vue/runtime-core';
import LayoutContent from '@/layout/layout-content.vue'; import LayoutContent from '@/layout/layout-content.vue';
import ComplexTable from '@/components/complex-table/index.vue'; import ComplexTable from '@/components/complex-table/index.vue';
import i18n from '@/lang'; import i18n from '@/lang';
import { GetFilesList } from '@/api/modules/files'; import { GetFilesList, GetFilesTree } from '@/api/modules/files';
import { dateFromat } from '@/utils/util'; import { dateFromat } from '@/utils/util';
import { File } from '@/api/interface/file'; import { File } from '@/api/interface/file';
import BreadCrumbs from '@/components/bread-crumbs/index.vue'; import BreadCrumbs from '@/components/bread-crumbs/index.vue';
import BreadCrumbItem from '@/components/bread-crumbs/bread-crumbs-item.vue'; import BreadCrumbItem from '@/components/bread-crumbs/bread-crumbs-item.vue';
interface Tree {
id: number;
label: string;
isDir: Boolean;
children?: Tree[];
}
let data = ref(); let data = ref();
let selects = ref<any>([]); let selects = ref<any>([]);
let req = reactive({ path: '/', expand: true }); let req = reactive({ path: '/', expand: true });
let loading = ref<boolean>(false); let loading = ref<boolean>(false);
let treeLoading = ref<boolean>(false);
let paths = ref<string[]>([]); let paths = ref<string[]>([]);
let fileTree = ref<File.FileTree[]>([]);
const defaultProps = {
children: 'children',
label: 'name',
};
const paginationConfig = reactive({ const paginationConfig = reactive({
page: 1, page: 1,
pageSize: 5, pageSize: 5,
@ -135,9 +147,9 @@ const buttons = [
}, },
]; ];
const search = (req: File.ReqFile) => { const search = async (req: File.ReqFile) => {
loading.value = true; loading.value = true;
GetFilesList(req) await GetFilesList(req)
.then((res) => { .then((res) => {
data.value = res.data.items; data.value = res.data.items;
req.path = res.data.path; req.path = res.data.path;
@ -176,53 +188,36 @@ const jump = async (index: number) => {
search(req); search(req);
}; };
const dataSource = ref<Tree[]>([ const getTree = async (req: File.ReqFile, node: File.FileTree | null) => {
{ treeLoading.value = true;
id: 1, await GetFilesTree(req)
label: 'var', .then((res) => {
isDir: true, if (node) {
children: [ if (res.data.length > 0) {
{ node.children = res.data[0].children;
id: 4, }
label: 'log', } else {
isDir: true, fileTree.value = res.data;
children: [ }
{ search(req);
id: 9, })
isDir: false, .finally(() => {
label: 'ko.log', treeLoading.value = false;
}, });
{ };
id: 10,
isDir: false,
label: 'kubepi.log',
},
],
},
],
},
{
id: 2,
label: 'opt',
isDir: true,
children: [
{
id: 5,
isDir: false,
label: 'app.conf',
},
{
id: 6,
isDir: false,
label: 'test.txt',
},
],
},
]);
onMounted(() => { const loadNode = (node: any, resolve: (data: File.FileTree[]) => void) => {
search(req); console.log(node.id);
}); if (!node.hasChildNodes) {
if (node.data.path) {
req.path = node.data.path;
getTree(req, node.data);
} else {
getTree(req, null);
}
}
resolve([]);
};
</script> </script>
<style> <style>