mirror of https://github.com/1Panel-dev/1Panel
feat: 文件管理页面
parent
2964f83d68
commit
07af78758f
File diff suppressed because it is too large
Load Diff
|
@ -25,9 +25,9 @@
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
"echarts": "^5.3.0",
|
"echarts": "^5.3.0",
|
||||||
"echarts-liquidfill": "^3.1.0",
|
"echarts-liquidfill": "^3.1.0",
|
||||||
"element-plus": "^2.2.6",
|
|
||||||
"fit2cloud-ui-plus": "^0.0.1-beta.12",
|
|
||||||
"js-base64": "^3.7.2",
|
"js-base64": "^3.7.2",
|
||||||
|
"element-plus": "^2.2.13",
|
||||||
|
"fit2cloud-ui-plus": "^0.0.1-beta.15",
|
||||||
"js-md5": "^0.7.3",
|
"js-md5": "^0.7.3",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.0.12",
|
"pinia": "^2.0.12",
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { CommonModel } from '.';
|
||||||
|
export namespace File {
|
||||||
|
export interface File extends CommonModel {
|
||||||
|
name: string;
|
||||||
|
mode: number;
|
||||||
|
user: string;
|
||||||
|
group: string;
|
||||||
|
updateDate: string;
|
||||||
|
isDir: boolean;
|
||||||
|
isLink: boolean;
|
||||||
|
path: string;
|
||||||
|
size: number;
|
||||||
|
accessTime: string;
|
||||||
|
changeTime: string;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"name": "var",
|
||||||
|
"isDir": true,
|
||||||
|
"mode": 775,
|
||||||
|
"user": "root",
|
||||||
|
"group": "root",
|
||||||
|
"size": 2048,
|
||||||
|
"updateTime": "2022-08-11T11:05:22.001+08:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test.txt",
|
||||||
|
"isDir": false,
|
||||||
|
"mode": 775,
|
||||||
|
"user": "root",
|
||||||
|
"group": "root",
|
||||||
|
"size": 4096,
|
||||||
|
"updateTime": "2022-08-11T11:05:22.001+08:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 2
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
// import { File } from '@/api/interface/file';
|
||||||
|
import files from '@/api/interface/files.json';
|
||||||
|
|
||||||
|
export const GetFilesList = () => {
|
||||||
|
// return http.post<Login.ResLogin>(`/auth/login`, params);
|
||||||
|
return files;
|
||||||
|
};
|
|
@ -43,4 +43,11 @@ const svgClass = computed(() => {
|
||||||
padding-left: 0.3em;
|
padding-left: 0.3em;
|
||||||
padding-right: 0.3em;
|
padding-right: 0.3em;
|
||||||
}
|
}
|
||||||
|
.table-icon {
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
position: relative;
|
||||||
|
fill: currentColor;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
commons: {
|
commons: {
|
||||||
button: {
|
button: {
|
||||||
|
create: '新建',
|
||||||
create: '创建',
|
create: '创建',
|
||||||
add: '添加',
|
add: '添加',
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
|
@ -151,4 +152,27 @@ export default {
|
||||||
request: '请求',
|
request: '请求',
|
||||||
response: '响应',
|
response: '响应',
|
||||||
},
|
},
|
||||||
|
file: {
|
||||||
|
dir: '文件夹',
|
||||||
|
upload: '上传',
|
||||||
|
download: '下载',
|
||||||
|
fileName: '文件名',
|
||||||
|
search: '查找',
|
||||||
|
mode: '权限',
|
||||||
|
owner: '所有者',
|
||||||
|
file: '文件',
|
||||||
|
remoteFile: '远程下载',
|
||||||
|
share: '分享',
|
||||||
|
sync: '数据同步',
|
||||||
|
size: '大小',
|
||||||
|
updateTime: '修改时间',
|
||||||
|
open: '打开',
|
||||||
|
rename: '重命名',
|
||||||
|
role: '权限',
|
||||||
|
info: '属性',
|
||||||
|
linkFile: '软连接文件',
|
||||||
|
terminal: '终端',
|
||||||
|
shareList: '分享列表',
|
||||||
|
zip: '压缩',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
:header="header"
|
:header="header"
|
||||||
v-if="showBack"
|
v-if="showBack"
|
||||||
></back-button>
|
></back-button>
|
||||||
{{ header }}
|
<span v-else> {{ header }}</span>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
<div class="content-container__toolbar" v-if="slots.toolbar">
|
<div class="content-container__toolbar" v-if="slots.toolbar">
|
||||||
|
|
|
@ -7,10 +7,11 @@ import '@/assets/iconfont/iconfont.js';
|
||||||
import ElementPlus from 'element-plus';
|
import ElementPlus from 'element-plus';
|
||||||
import Fit2CloudPlus from 'fit2cloud-ui-plus';
|
import Fit2CloudPlus from 'fit2cloud-ui-plus';
|
||||||
import * as Icons from '@element-plus/icons-vue';
|
import * as Icons from '@element-plus/icons-vue';
|
||||||
import 'element-plus/dist/index.css';
|
|
||||||
import 'element-plus/theme-chalk/dark/css-vars.css';
|
|
||||||
import '@/styles/element-dark.scss';
|
import '@/styles/element-dark.scss';
|
||||||
import '@/styles/element.scss';
|
import '@/styles/element.scss';
|
||||||
|
import 'element-plus/dist/index.css';
|
||||||
|
import 'element-plus/theme-chalk/dark/css-vars.css';
|
||||||
|
import 'fit2cloud-ui-plus/src/styles/index.scss';
|
||||||
import directives from '@/directives/index';
|
import directives from '@/directives/index';
|
||||||
import router from '@/routers/index';
|
import router from '@/routers/index';
|
||||||
import I18n from '@/lang/index';
|
import I18n from '@/lang/index';
|
||||||
|
|
|
@ -140,6 +140,20 @@ html.dark {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
--el-table-border-color: #696969;
|
||||||
|
//fit2cloud-ui 自定义
|
||||||
|
--el-table-text-color: #cfcfcf;
|
||||||
|
|
||||||
|
// $table-header-bgColor: #f5f6f7 !default;
|
||||||
|
.fu-table-header th {
|
||||||
|
border-top: 1px solid var(--el-table-border-color);
|
||||||
|
font-weight: 500 !important;
|
||||||
|
color: var(--el-text-color-regular) !important;
|
||||||
|
background-color: var(--el-bg-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// el-drawer
|
// el-drawer
|
||||||
.el-drawer {
|
.el-drawer {
|
||||||
.el-drawer__header {
|
.el-drawer__header {
|
||||||
|
|
|
@ -189,4 +189,4 @@
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
border: 0;
|
border: 0;
|
||||||
// box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
// box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,190 @@
|
||||||
<template>
|
<template>
|
||||||
<LayoutContent></LayoutContent>
|
<LayoutContent :header="$t('menu.files')">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-tree :data="dataSource" show-checkbox 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>
|
||||||
|
<el-col :span="18">
|
||||||
|
<div class="path">
|
||||||
|
<el-breadcrumb class="child" :separator-icon="ArrowRight">
|
||||||
|
<el-breadcrumb-item>root</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>var</el-breadcrumb-item>
|
||||||
|
<el-breadcrumb-item>log</el-breadcrumb-item>
|
||||||
|
</el-breadcrumb>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" :data="data">
|
||||||
|
<template #toolbar>
|
||||||
|
<el-dropdown split-button type="primary">
|
||||||
|
{{ $t('commons.button.create') }}
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<svg-icon iconName="p-file-folder"></svg-icon>{{ $t('file.dir') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<svg-icon iconName="p-file-normal"></svg-icon>{{ $t('file.file') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<svg-icon iconName="p-file-normal"></svg-icon>{{ $t('file.linkFile') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
<el-button type="primary" plain> {{ $t('file.upload') }}</el-button>
|
||||||
|
<el-button type="primary" plain> {{ $t('file.search') }}</el-button>
|
||||||
|
<el-button type="primary" plain> {{ $t('file.remoteFile') }}</el-button>
|
||||||
|
<el-button type="primary" plain> {{ $t('file.sync') }}</el-button>
|
||||||
|
<el-button type="primary" plain> {{ $t('file.terminal') }}</el-button>
|
||||||
|
<el-button type="primary" plain> {{ $t('file.shareList') }}</el-button>
|
||||||
|
</template>
|
||||||
|
<el-table-column :label="$t('commons.table.name')" min-width="120" fix>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<svg-icon v-if="row.isDir" className="table-icon" iconName="p-file-folder"></svg-icon>
|
||||||
|
<svg-icon v-else className="table-icon" iconName="p-file-normal"></svg-icon>
|
||||||
|
<el-link :underline="false">{{ row.name }}</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('file.mode')" prop="mode"> </el-table-column>
|
||||||
|
<el-table-column :label="$t('file.owner')" prop="user"> </el-table-column>
|
||||||
|
<el-table-column :label="$t('file.size')" prop="size"> </el-table-column>
|
||||||
|
<el-table-column :label="$t('file.updateTime')" prop="updateTime" :formatter="dateFromat">
|
||||||
|
</el-table-column>
|
||||||
|
|
||||||
|
<fu-table-operations
|
||||||
|
min-width="300"
|
||||||
|
:ellipsis="4"
|
||||||
|
:buttons="buttons"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fixed="right"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</LayoutContent>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import { onMounted, 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 { dateFromat } from '@/utils/util';
|
||||||
|
import { ArrowRight } from '@element-plus/icons-vue';
|
||||||
|
interface Tree {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
isDir: Boolean;
|
||||||
|
children?: Tree[];
|
||||||
|
}
|
||||||
|
let data = ref();
|
||||||
|
let selects = ref<any>([]);
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 5,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('file.open'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('file.mode'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('file.zip'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('file.rename'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('file.info'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
const res = await GetFilesList();
|
||||||
|
data.value = res.data.items;
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
};
|
||||||
|
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.path {
|
||||||
|
margin-top: -50px;
|
||||||
|
height: 30px;
|
||||||
|
width: 800px;
|
||||||
|
background-color: #f1f1f1;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.child {
|
||||||
|
position: relative;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -8,10 +8,7 @@ import viteCompression from 'vite-plugin-compression';
|
||||||
import VueSetupExtend from 'vite-plugin-vue-setup-extend';
|
import VueSetupExtend from 'vite-plugin-vue-setup-extend';
|
||||||
import eslintPlugin from 'vite-plugin-eslint';
|
import eslintPlugin from 'vite-plugin-eslint';
|
||||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||||
import importToCDN from 'vite-plugin-cdn-import';
|
|
||||||
// import AutoImport from "unplugin-auto-import/vite";
|
|
||||||
// import Components from "unplugin-vue-components/vite";
|
|
||||||
// import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
|
|
||||||
import DefineOptions from 'unplugin-vue-define-options/vite';
|
import DefineOptions from 'unplugin-vue-define-options/vite';
|
||||||
|
|
||||||
// @see: https://vitejs.dev/config/
|
// @see: https://vitejs.dev/config/
|
||||||
|
@ -59,37 +56,14 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||||
}),
|
}),
|
||||||
DefineOptions(),
|
DefineOptions(),
|
||||||
// * EsLint 报错信息显示在浏览器界面上
|
// * EsLint 报错信息显示在浏览器界面上
|
||||||
eslintPlugin(),
|
eslintPlugin({
|
||||||
|
exclude: ['**/*.js'],
|
||||||
|
}),
|
||||||
// * vite 可以使用 jsx/tsx 语法
|
// * vite 可以使用 jsx/tsx 语法
|
||||||
vueJsx(),
|
vueJsx(),
|
||||||
// * name 可以写在 script 标签上
|
// * name 可以写在 script 标签上
|
||||||
VueSetupExtend(),
|
VueSetupExtend(),
|
||||||
// * demand import element(如果使用了cdn引入,没必要使用element自动导入了)
|
|
||||||
// AutoImport({
|
|
||||||
// resolvers: [ElementPlusResolver()]
|
|
||||||
// }),
|
|
||||||
// Components({
|
|
||||||
// resolvers: [ElementPlusResolver()]
|
|
||||||
// }),
|
|
||||||
// * cdn 引入(vue、element-plus)
|
|
||||||
importToCDN({
|
|
||||||
modules: [
|
|
||||||
// vue按需引入会导致依赖vue的插件出现问题(列如:pinia/vuex)
|
|
||||||
// {
|
|
||||||
// name: "vue",
|
|
||||||
// var: "Vue",
|
|
||||||
// path: "https://unpkg.com/vue@next"
|
|
||||||
// },
|
|
||||||
// 使用cdn引入element-plus时,开发环境还是需要在main.js中引入element-plus,可以不用引入css
|
|
||||||
// {
|
|
||||||
// name: "element-plus",
|
|
||||||
// var: "ElementPlus",
|
|
||||||
// path: "https://unpkg.com/element-plus",
|
|
||||||
// css: "https://unpkg.com/element-plus/dist/index.css"
|
|
||||||
// }
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
// * 是否生成包预览
|
|
||||||
viteEnv.VITE_REPORT && visualizer(),
|
viteEnv.VITE_REPORT && visualizer(),
|
||||||
// * gzip compress
|
// * gzip compress
|
||||||
viteEnv.VITE_BUILD_GZIP &&
|
viteEnv.VITE_BUILD_GZIP &&
|
||||||
|
@ -104,18 +78,9 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||||
esbuild: {
|
esbuild: {
|
||||||
pure: viteEnv.VITE_DROP_CONSOLE ? ['console.log', 'debugger'] : [],
|
pure: viteEnv.VITE_DROP_CONSOLE ? ['console.log', 'debugger'] : [],
|
||||||
},
|
},
|
||||||
// build configure
|
|
||||||
build: {
|
build: {
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
// esbuild 打包更快,但是不能去除 console.log
|
|
||||||
minify: 'esbuild',
|
minify: 'esbuild',
|
||||||
// minify: "terser",
|
|
||||||
// terserOptions: {
|
|
||||||
// compress: {
|
|
||||||
// drop_console: viteEnv.VITE_DROP_CONSOLE,
|
|
||||||
// drop_debugger: true
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
output: {
|
output: {
|
||||||
// Static resource classification and packaging
|
// Static resource classification and packaging
|
||||||
|
|
Loading…
Reference in New Issue