mirror of
https://gitee.com/fit2cloud-feizhiyun/1Panel.git
synced 2024-11-30 19:08:06 +08:00
feat: 文件夹树形列表实现
This commit is contained in:
parent
58ea83c030
commit
8faff6d386
@ -20,3 +20,17 @@ func (b *BaseApi) ListFiles(c *gin.Context) {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
@ -9,3 +9,9 @@ type FileOption struct {
|
||||
type FileInfo struct {
|
||||
files.FileInfo
|
||||
}
|
||||
|
||||
type FileTree struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Children []FileTree `json:"children"`
|
||||
}
|
||||
|
@ -25,3 +25,24 @@ func (f FileService) GetFileList(op dto.FileOption) (dto.FileInfo, error) {
|
||||
fileInfo.FileInfo = *info
|
||||
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
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
|
||||
baseApi := v1.ApiGroupApp.BaseApi
|
||||
{
|
||||
fileRouter.POST("/search", baseApi.ListFiles)
|
||||
fileRouter.POST("/tree", baseApi.GetFileTree)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { ResultData } from '@/api/interface';
|
||||
import { ResultEnum } from '@/enums/http-enum';
|
||||
@ -31,7 +31,7 @@ class RequestHttp {
|
||||
};
|
||||
}
|
||||
axiosCanceler.addPending(config);
|
||||
config.headers!.noLoading || showFullScreenLoading();
|
||||
// config.headers!.noLoading || showFullScreenLoading();
|
||||
return {
|
||||
...config,
|
||||
};
|
||||
@ -48,7 +48,7 @@ class RequestHttp {
|
||||
globalStore.setCsrfToken(response.headers['x-csrf-token']);
|
||||
}
|
||||
axiosCanceler.removePending(config);
|
||||
tryHideFullScreenLoading();
|
||||
// tryHideFullScreenLoading();
|
||||
if (data.code == ResultEnum.OVERDUE || data.code == ResultEnum.FORBIDDEN) {
|
||||
ElMessage.error(data.msg);
|
||||
router.replace({
|
||||
@ -64,7 +64,7 @@ class RequestHttp {
|
||||
},
|
||||
async (error: AxiosError) => {
|
||||
const { response } = error;
|
||||
tryHideFullScreenLoading();
|
||||
// tryHideFullScreenLoading();
|
||||
if (error.message.indexOf('timeout') !== -1) ElMessage.error('请求超时!请您稍后重试');
|
||||
if (response) checkStatus(response.status);
|
||||
if (!window.navigator.onLine) router.replace({ path: '/500' });
|
||||
|
@ -21,4 +21,11 @@ export namespace File {
|
||||
search?: string;
|
||||
expand: boolean;
|
||||
}
|
||||
|
||||
export interface FileTree {
|
||||
name: string;
|
||||
isDir: Boolean;
|
||||
path: string;
|
||||
children?: FileTree[];
|
||||
}
|
||||
}
|
||||
|
@ -4,3 +4,7 @@ import http from '@/api';
|
||||
export const GetFilesList = (params: File.ReqFile) => {
|
||||
return http.post<File.File>('files/search', params);
|
||||
};
|
||||
|
||||
export const GetFilesTree = (params: File.ReqFile) => {
|
||||
return http.post<File.FileTree[]>('files/tree', params);
|
||||
};
|
||||
|
@ -1,19 +1,28 @@
|
||||
<template>
|
||||
<LayoutContent :header="$t('menu.files')">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-tree :data="dataSource" node-key="id">
|
||||
<template #default="{ node }">
|
||||
<el-icon v-if="node.data.isDir && node.expanded"><FolderOpened /></el-icon>
|
||||
<el-icon v-if="node.data.isDir && !node.expanded"><Folder /></el-icon>
|
||||
<el-icon v-if="!node.data.isDir"><Document /></el-icon>
|
||||
<span class="custom-tree-node">
|
||||
<span>{{ node.data.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
<el-col :span="5">
|
||||
<el-scrollbar height="800px">
|
||||
<el-tree
|
||||
:data="fileTree"
|
||||
:props="defaultProps"
|
||||
:load="loadNode"
|
||||
lazy
|
||||
node-key="id"
|
||||
v-loading="treeLoading"
|
||||
>
|
||||
<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 :span="18">
|
||||
|
||||
<el-col :span="19">
|
||||
<div class="path">
|
||||
<BreadCrumbs>
|
||||
<BreadCrumbItem @click="jump(-1)" :right="paths.length == 0">root</BreadCrumbItem>
|
||||
@ -30,7 +39,7 @@
|
||||
:pagination-config="paginationConfig"
|
||||
v-model:selects="selects"
|
||||
:data="data"
|
||||
:loading="loading"
|
||||
v-loading="loading"
|
||||
>
|
||||
<template #toolbar>
|
||||
<el-dropdown split-button type="primary">
|
||||
@ -89,26 +98,29 @@
|
||||
</template>
|
||||
|
||||
<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 ComplexTable from '@/components/complex-table/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import { GetFilesList } from '@/api/modules/files';
|
||||
import { GetFilesList, GetFilesTree } from '@/api/modules/files';
|
||||
import { dateFromat } from '@/utils/util';
|
||||
import { File } from '@/api/interface/file';
|
||||
import BreadCrumbs from '@/components/bread-crumbs/index.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 selects = ref<any>([]);
|
||||
let req = reactive({ path: '/', expand: true });
|
||||
let loading = ref<boolean>(false);
|
||||
let treeLoading = ref<boolean>(false);
|
||||
let paths = ref<string[]>([]);
|
||||
let fileTree = ref<File.FileTree[]>([]);
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
};
|
||||
|
||||
const paginationConfig = reactive({
|
||||
page: 1,
|
||||
pageSize: 5,
|
||||
@ -135,9 +147,9 @@ const buttons = [
|
||||
},
|
||||
];
|
||||
|
||||
const search = (req: File.ReqFile) => {
|
||||
const search = async (req: File.ReqFile) => {
|
||||
loading.value = true;
|
||||
GetFilesList(req)
|
||||
await GetFilesList(req)
|
||||
.then((res) => {
|
||||
data.value = res.data.items;
|
||||
req.path = res.data.path;
|
||||
@ -176,53 +188,36 @@ const jump = async (index: number) => {
|
||||
search(req);
|
||||
};
|
||||
|
||||
const dataSource = ref<Tree[]>([
|
||||
{
|
||||
id: 1,
|
||||
label: 'var',
|
||||
isDir: true,
|
||||
children: [
|
||||
{
|
||||
id: 4,
|
||||
label: 'log',
|
||||
isDir: true,
|
||||
children: [
|
||||
{
|
||||
id: 9,
|
||||
isDir: false,
|
||||
label: 'ko.log',
|
||||
},
|
||||
{
|
||||
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',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
const getTree = async (req: File.ReqFile, node: File.FileTree | null) => {
|
||||
treeLoading.value = true;
|
||||
await GetFilesTree(req)
|
||||
.then((res) => {
|
||||
if (node) {
|
||||
if (res.data.length > 0) {
|
||||
node.children = res.data[0].children;
|
||||
}
|
||||
} else {
|
||||
fileTree.value = res.data;
|
||||
}
|
||||
search(req);
|
||||
})
|
||||
.finally(() => {
|
||||
treeLoading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search(req);
|
||||
});
|
||||
const loadNode = (node: any, resolve: (data: File.FileTree[]) => void) => {
|
||||
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>
|
||||
|
||||
<style>
|
||||
|
Loading…
Reference in New Issue
Block a user