mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-25 19:49:14 +08:00
feat: 适配面包屑长度
This commit is contained in:
parent
3c4dd6249a
commit
eb046090a5
1
frontend/components.d.ts
vendored
1
frontend/components.d.ts
vendored
@ -10,6 +10,7 @@ declare module 'vue' {
|
||||
AppLayout: typeof import('./src/components/app-layout/index.vue')['default']
|
||||
AppStatus: typeof import('./src/components/app-status/index.vue')['default']
|
||||
BackButton: typeof import('./src/components/back-button/index.vue')['default']
|
||||
Bread: typeof import('./src/components/bread-crumbs/bread.vue')['default']
|
||||
BreadCrumbs: typeof import('./src/components/bread-crumbs/index.vue')['default']
|
||||
BreadCrumbsItem: typeof import('./src/components/bread-crumbs/bread-crumbs-item.vue')['default']
|
||||
CardWithHeader: typeof import('./src/components/card-with-header/index.vue')['default']
|
||||
|
@ -1,12 +1,23 @@
|
||||
<template>
|
||||
<div class="bread-crumbs-item">
|
||||
<span class="bread-crumbs-item" ref="item">
|
||||
<el-link><slot></slot></el-link>
|
||||
<i v-if="!props.right" :class="'panel p-arrow-right'"></i>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
right: Boolean,
|
||||
});
|
||||
|
||||
const item = ref();
|
||||
defineExpose({ item });
|
||||
</script>
|
||||
|
||||
<style lang="scss" setup>
|
||||
.bread-crumbs-item {
|
||||
margin-top: 3px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,6 +1,12 @@
|
||||
<template>
|
||||
<div class="bread-crumbs"><slot></slot></div>
|
||||
<span class="bread-crumbs" ref="bread"><slot></slot></span>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
|
||||
const bread = ref();
|
||||
defineExpose({ bread });
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.bread-crumbs {
|
||||
display: flex;
|
||||
|
@ -697,6 +697,7 @@ export default {
|
||||
downloading: '正在下载...',
|
||||
infoDetail: '文件属性',
|
||||
type: '类型',
|
||||
root: '根目录',
|
||||
},
|
||||
setting: {
|
||||
all: '全部',
|
||||
|
@ -50,6 +50,7 @@ let form = ref({
|
||||
const em = defineEmits(['close']);
|
||||
|
||||
const handleClose = () => {
|
||||
open.value = false;
|
||||
if (editor) {
|
||||
editor.dispose();
|
||||
}
|
||||
|
@ -1,62 +1,66 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col :span="2">
|
||||
<div>
|
||||
<el-button :icon="Back" @click="jump(paths.length - 2)" circle :disabled="paths.length == 0" />
|
||||
<el-button :icon="Refresh" circle @click="search" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="22">
|
||||
<div class="path">
|
||||
<BreadCrumbs>
|
||||
<BreadCrumbItem @click="jump(-1)" :right="paths.length == 0">/</BreadCrumbItem>
|
||||
<BreadCrumbItem
|
||||
v-for="(item, key) in paths"
|
||||
:key="key"
|
||||
@click="jump(key)"
|
||||
:right="key == paths.length - 1"
|
||||
>
|
||||
{{ item }}
|
||||
</BreadCrumbItem>
|
||||
</BreadCrumbs>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<LayoutContent :title="$t('file.file')" v-loading="loading">
|
||||
<template #toolbar>
|
||||
<el-dropdown @command="handleCreate">
|
||||
<el-button type="primary">
|
||||
{{ $t('commons.button.create') }}
|
||||
<el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-col :span="2">
|
||||
<div>
|
||||
<el-button
|
||||
:icon="Back"
|
||||
@click="jump(paths[paths.length - 2].url)"
|
||||
circle
|
||||
:disabled="paths.length == 0"
|
||||
/>
|
||||
<el-button :icon="Refresh" circle @click="search" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="22">
|
||||
<div class="path" ref="pathRef">
|
||||
<span ref="breadCrumbRef">
|
||||
<span class="root">
|
||||
<el-link @click="jump('/')">{{ $t('file.root') }}</el-link>
|
||||
</span>
|
||||
<span v-for="item in paths" :key="item.url">
|
||||
<span class="split">></span>
|
||||
<el-link @click="jump(item.url)">{{ item.name }}</el-link>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<LayoutContent :title="$t('file.file')" v-loading="loading">
|
||||
<template #toolbar>
|
||||
<el-dropdown @command="handleCreate">
|
||||
<el-button type="primary">
|
||||
{{ $t('commons.button.create') }}
|
||||
<el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="dir">
|
||||
<svg-icon iconName="p-file-folder"></svg-icon>
|
||||
{{ $t('file.dir') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="file">
|
||||
<svg-icon iconName="p-file-normal"></svg-icon>
|
||||
{{ $t('file.file') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button plain @click="openUpload" style="margin-left: 10px">{{ $t('file.upload') }}</el-button>
|
||||
<el-button plain @click="openWget">{{ $t('file.remoteFile') }}</el-button>
|
||||
<el-button plain @click="openMove('copy')" :disabled="selects.length === 0">
|
||||
{{ $t('file.copy') }}
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="dir">
|
||||
<svg-icon iconName="p-file-folder"></svg-icon>
|
||||
{{ $t('file.dir') }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="file">
|
||||
<svg-icon iconName="p-file-normal"></svg-icon>
|
||||
{{ $t('file.file') }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
<el-button plain @click="openUpload" style="margin-left: 10px">{{ $t('file.upload') }}</el-button>
|
||||
<el-button plain @click="openWget">{{ $t('file.remoteFile') }}</el-button>
|
||||
<el-button plain @click="openMove('copy')" :disabled="selects.length === 0">
|
||||
{{ $t('file.copy') }}
|
||||
</el-button>
|
||||
<el-button plain @click="openMove('cut')" :disabled="selects.length === 0">
|
||||
{{ $t('file.move') }}
|
||||
</el-button>
|
||||
<el-button plain @click="openCompress(selects)" :disabled="selects.length === 0">
|
||||
{{ $t('file.compress') }}
|
||||
</el-button>
|
||||
<el-button plain @click="openDownload" :disabled="selects.length === 0">
|
||||
{{ $t('file.download') }}
|
||||
</el-button>
|
||||
<!-- <div class="search-button">
|
||||
<el-button plain @click="openMove('cut')" :disabled="selects.length === 0">
|
||||
{{ $t('file.move') }}
|
||||
</el-button>
|
||||
<el-button plain @click="openCompress(selects)" :disabled="selects.length === 0">
|
||||
{{ $t('file.compress') }}
|
||||
</el-button>
|
||||
<el-button plain @click="openDownload" :disabled="selects.length === 0">
|
||||
{{ $t('file.download') }}
|
||||
</el-button>
|
||||
<!-- <div class="search-button">
|
||||
<el-input
|
||||
clearable
|
||||
@clear="search()"
|
||||
@ -66,73 +70,79 @@
|
||||
:placeholder="$t('commons.button.search')"
|
||||
></el-input>
|
||||
</div> -->
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" :data="data" @search="search">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column :label="$t('commons.table.name')" min-width="250" fix show-overflow-tooltip>
|
||||
<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="getIconName(row.extension)"></svg-icon>
|
||||
<el-link :underline="false" @click="open(row)">{{ row.name }}</el-link>
|
||||
<span v-if="row.isSymlink">-> {{ row.linkPath }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.mode')" prop="mode">
|
||||
<template #default="{ row }">
|
||||
<el-link :underline="false" @click="openMode(row)">{{ row.mode }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.user')" prop="user" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('file.group')" prop="group"></el-table-column>
|
||||
<el-table-column :label="$t('file.size')" prop="size">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.isDir">
|
||||
<el-button type="primary" link small @click="getDirSize(row)">
|
||||
<span v-if="row.dirSize == undefined">
|
||||
{{ $t('file.calculate') }}
|
||||
</span>
|
||||
<span v-else>{{ getFileSize(row.dirSize) }}</span>
|
||||
</el-button>
|
||||
</span>
|
||||
<span v-else>{{ getFileSize(row.size) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('file.updateTime')"
|
||||
prop="modTime"
|
||||
:formatter="dateFormat"
|
||||
min-width="150"
|
||||
show-overflow-tooltip
|
||||
></el-table-column>
|
||||
</template>
|
||||
<template #main>
|
||||
<ComplexTable
|
||||
:pagination-config="paginationConfig"
|
||||
v-model:selects="selects"
|
||||
:data="data"
|
||||
@search="search"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column :label="$t('commons.table.name')" min-width="250" fix show-overflow-tooltip>
|
||||
<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="getIconName(row.extension)"></svg-icon>
|
||||
<el-link :underline="false" @click="open(row)">{{ row.name }}</el-link>
|
||||
<span v-if="row.isSymlink">-> {{ row.linkPath }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.mode')" prop="mode">
|
||||
<template #default="{ row }">
|
||||
<el-link :underline="false" @click="openMode(row)">{{ row.mode }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.user')" prop="user" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :label="$t('file.group')" prop="group"></el-table-column>
|
||||
<el-table-column :label="$t('file.size')" prop="size">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.isDir">
|
||||
<el-button type="primary" link small @click="getDirSize(row)">
|
||||
<span v-if="row.dirSize == undefined">
|
||||
{{ $t('file.calculate') }}
|
||||
</span>
|
||||
<span v-else>{{ getFileSize(row.dirSize) }}</span>
|
||||
</el-button>
|
||||
</span>
|
||||
<span v-else>{{ getFileSize(row.size) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('file.updateTime')"
|
||||
prop="modTime"
|
||||
:formatter="dateFormat"
|
||||
min-width="150"
|
||||
show-overflow-tooltip
|
||||
></el-table-column>
|
||||
|
||||
<fu-table-operations
|
||||
:ellipsis="1"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
fixed="right"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
</template>
|
||||
<fu-table-operations
|
||||
:ellipsis="1"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
fixed="right"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
</template>
|
||||
|
||||
<CreateFile ref="createRef" @close="search" />
|
||||
<ChangeRole ref="roleRef" @close="search" />
|
||||
<Compress ref="compressRef" @close="search" />
|
||||
<Decompress ref="deCompressRef" @close="search" />
|
||||
<CodeEditor ref="codeEditorRef" @close="search" />
|
||||
<FileRename ref="renameRef" @close="search" />
|
||||
<Upload ref="uploadRef" @close="search" />
|
||||
<Wget ref="wgetRef" @close="closeWget" />
|
||||
<Move ref="moveRef" @close="search" />
|
||||
<Download ref="downloadRef" @close="search" />
|
||||
<Process :open="processPage.open" @close="closeProcess" />
|
||||
<Detail ref="detailRef" />
|
||||
</LayoutContent>
|
||||
<CreateFile ref="createRef" @close="search" />
|
||||
<ChangeRole ref="roleRef" @close="search" />
|
||||
<Compress ref="compressRef" @close="search" />
|
||||
<Decompress ref="deCompressRef" @close="search" />
|
||||
<CodeEditor ref="codeEditorRef" @close="search" />
|
||||
<FileRename ref="renameRef" @close="search" />
|
||||
<Upload ref="uploadRef" @close="search" />
|
||||
<Wget ref="wgetRef" @close="closeWget" />
|
||||
<Move ref="moveRef" @close="search" />
|
||||
<Download ref="downloadRef" @close="search" />
|
||||
<Process :open="processPage.open" @close="closeProcess" />
|
||||
<Detail ref="detailRef" />
|
||||
</LayoutContent>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from '@vue/runtime-core';
|
||||
import { nextTick, onMounted, reactive, ref } from '@vue/runtime-core';
|
||||
import { GetFilesList, DeleteFile, GetFileContent, ComputeDirSize } from '@/api/modules/files';
|
||||
import { computeSize, dateFormat, getIcon, getRandomStr } from '@/utils/util';
|
||||
import { File } from '@/api/interface/file';
|
||||
@ -141,8 +151,6 @@ import { ElMessage } from 'element-plus';
|
||||
import LayoutContent from '@/layout/layout-content.vue';
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import BreadCrumbs from '@/components/bread-crumbs/index.vue';
|
||||
import BreadCrumbItem from '@/components/bread-crumbs/bread-crumbs-item.vue';
|
||||
import CreateFile from './create/index.vue';
|
||||
import ChangeRole from './change-role/index.vue';
|
||||
import Compress from './compress/index.vue';
|
||||
@ -159,12 +167,18 @@ import Detail from './detail/index.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { Back, Refresh } from '@element-plus/icons-vue';
|
||||
|
||||
interface FilePaths {
|
||||
url: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
const router = useRouter();
|
||||
const data = ref();
|
||||
let selects = ref<any>([]);
|
||||
let req = reactive({ path: '/', expand: true, showHidden: false, page: 1, pageSize: 100 });
|
||||
let loading = ref(false);
|
||||
const paths = ref<string[]>([]);
|
||||
const paths = ref<FilePaths[]>([]);
|
||||
let pathWidth = ref(0);
|
||||
|
||||
const fileCreate = reactive({ path: '/', isDir: false, mode: 0o755 });
|
||||
const fileCompress = reactive({ files: [''], name: '', dst: '', operate: 'compress' });
|
||||
@ -189,6 +203,8 @@ const uploadRef = ref();
|
||||
const wgetRef = ref();
|
||||
const moveRef = ref();
|
||||
const downloadRef = ref();
|
||||
const pathRef = ref();
|
||||
const breadCrumbRef = ref();
|
||||
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
@ -205,13 +221,6 @@ const search = async () => {
|
||||
paginationConfig.total = res.data.itemTotal;
|
||||
data.value = res.data.items;
|
||||
req.path = res.data.path;
|
||||
const pathArray = req.path.split('/');
|
||||
paths.value = [];
|
||||
for (const p of pathArray) {
|
||||
if (p != '') {
|
||||
paths.value.push(p);
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
@ -221,32 +230,58 @@ const search = async () => {
|
||||
const open = async (row: File.File) => {
|
||||
if (row.isDir) {
|
||||
const name = row.name;
|
||||
paths.value.push(name);
|
||||
if (req.path.endsWith('/')) {
|
||||
req.path = req.path + name;
|
||||
} else {
|
||||
req.path = req.path + '/' + name;
|
||||
}
|
||||
search();
|
||||
paths.value.push({
|
||||
url: req.path,
|
||||
name: name,
|
||||
});
|
||||
|
||||
jump(req.path);
|
||||
} else {
|
||||
openCodeEditor(row);
|
||||
}
|
||||
};
|
||||
|
||||
const jump = async (index: number) => {
|
||||
let path = '/';
|
||||
if (index != -1) {
|
||||
const jPaths = paths.value.slice(0, index + 1);
|
||||
for (let i in jPaths) {
|
||||
if (path.endsWith('/')) {
|
||||
path = path + jPaths[i];
|
||||
const handlePath = () => {
|
||||
if (breadCrumbRef.value.offsetWidth > pathWidth.value) {
|
||||
paths.value.splice(0, 1);
|
||||
paths.value[0].name = '..';
|
||||
nextTick(function () {
|
||||
handlePath();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const jump = async (url: string) => {
|
||||
req.path = url;
|
||||
await search();
|
||||
getPaths(req.path);
|
||||
nextTick(function () {
|
||||
handlePath();
|
||||
});
|
||||
};
|
||||
|
||||
const getPaths = (reqPath: string) => {
|
||||
const pathArray = reqPath.split('/');
|
||||
paths.value = [];
|
||||
let base = '/';
|
||||
for (const p of pathArray) {
|
||||
if (p != '') {
|
||||
if (base.endsWith('/')) {
|
||||
base = base + p;
|
||||
} else {
|
||||
path = path + '/' + jPaths[i];
|
||||
base = base + '/' + p;
|
||||
}
|
||||
paths.value.push({
|
||||
url: base,
|
||||
name: p,
|
||||
});
|
||||
}
|
||||
}
|
||||
req.path = path;
|
||||
search();
|
||||
};
|
||||
|
||||
const handleCreate = (commnad: string) => {
|
||||
@ -424,14 +459,27 @@ const buttons = [
|
||||
onMounted(() => {
|
||||
if (router.currentRoute.value.query.path) {
|
||||
req.path = String(router.currentRoute.value.query.path);
|
||||
getPaths(req.path);
|
||||
}
|
||||
pathWidth.value = pathRef.value.offsetWidth * 0.7;
|
||||
search();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style scoped lang="scss">
|
||||
.path {
|
||||
border: 1px solid #ebeef5;
|
||||
background-color: #ffffff;
|
||||
height: 30px;
|
||||
border-radius: 2px !important;
|
||||
|
||||
.root {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.split {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.search-button {
|
||||
|
Loading…
Reference in New Issue
Block a user