1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: 增加上传文件夹功能 (#2661)

Refs https://github.com/1Panel-dev/1Panel/issues/1364
Refs https://github.com/1Panel-dev/1Panel/issues/2053
This commit is contained in:
zhengkunwang 2023-10-24 22:27:32 +08:00 committed by GitHub
parent f30f561723
commit b045261f16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 131 additions and 7 deletions

View File

@ -568,12 +568,13 @@ func (b *BaseApi) Size(c *gin.Context) {
}
func mergeChunks(fileName string, fileDir string, dstDir string, chunkCount int) error {
if _, err := os.Stat(path.Dir(dstDir)); err != nil && os.IsNotExist(err) {
if err = os.MkdirAll(path.Dir(dstDir), os.ModePerm); err != nil {
op := files.NewFileOp()
dstDir = strings.TrimSpace(dstDir)
if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) {
if err = op.CreateDir(dstDir, os.ModePerm); err != nil {
return err
}
}
targetFile, err := os.Create(filepath.Join(dstDir, fileName))
if err != nil {
return err

View File

@ -975,6 +975,7 @@ const message = {
favorite: 'favorites',
removeFavorite: 'Remove from favorites?',
addFavorite: 'Add to favorites',
clearList: 'Clear list',
},
ssh: {
autoStart: 'Auto Start',

View File

@ -936,6 +936,7 @@ const message = {
favorite: '收藏夾',
removeFavorite: '是否從收藏夾移出',
addFavorite: '加入收藏夾',
clearList: '清空列表',
},
ssh: {
autoStart: '開機自啟',

View File

@ -937,6 +937,7 @@ const message = {
favorite: '收藏夹',
removeFavorite: '是否从收藏夹移出',
addFavorite: '加入收藏夹子',
clearList: '清空列表',
},
ssh: {
autoStart: '开机自启',

View File

@ -53,6 +53,7 @@ const forceDelete = ref(false);
const acceptParams = (props: File.File[]) => {
files.value = props;
open.value = true;
forceDelete.value = false;
};
const onConfirm = () => {

View File

@ -9,6 +9,26 @@
<template #header>
<DrawerHeader :header="$t('file.upload')" :back="handleClose" />
</template>
<div class="button-container">
<el-dropdown @command="upload">
<el-button type="primary">
{{ $t('file.upload') }}
<el-icon><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="file">
{{ $t('file.file') }}
</el-dropdown-item>
<el-dropdown-item command="dir">
{{ $t('file.dir') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button @click="clearFiles">{{ $t('file.clearList') }}</el-button>
</div>
<el-upload
action="#"
drag
@ -17,8 +37,9 @@
:on-change="fileOnChange"
:on-exceed="handleExceed"
:on-success="hadleSuccess"
show-file-list
:show-file-list="false"
multiple
v-model:file-list="uploaderFiles"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
@ -30,6 +51,31 @@
<el-progress v-if="loading" text-inside :stroke-width="20" :percentage="uploadPrecent"></el-progress>
</template>
</el-upload>
<div>
<p
v-for="(item, index) in uploaderFiles"
:key="index"
class="file-item"
@mouseover="hoverIndex = index"
@mouseout="hoverIndex = null"
>
<el-icon class="file-icon"><Document /></el-icon>
<span v-if="item.raw.webkitRelativePath != ''">{{ item.raw.webkitRelativePath }}</span>
<span v-else>{{ item.name }}</span>
<span v-if="item.status === 'success'" class="success-icon">
<el-icon><Select /></el-icon>
</span>
<span v-else>
<el-button
class="delete-button"
type="primary"
link
@click="removeFile(index)"
:icon="Close"
></el-button>
</span>
</p>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
@ -42,12 +88,13 @@
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { nextTick, reactive, ref } from 'vue';
import { UploadFile, UploadFiles, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
import { ChunkUploadFileData, UploadFileData } from '@/api/modules/files';
import i18n from '@/lang';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message';
import { Close } from '@element-plus/icons-vue';
interface UploadFileProps {
path: string;
@ -66,13 +113,35 @@ const handleClose = () => {
uploadRef.value!.clearFiles();
em('close', false);
};
const state = reactive({
uploadEle: null,
});
const uploaderFiles = ref<UploadFiles>([]);
const isUploadFolder = ref(false);
const hoverIndex = ref(null);
const upload = (commnad: string) => {
if (commnad == 'dir') {
state.uploadEle.webkitdirectory = true;
} else {
state.uploadEle.webkitdirectory = false;
}
isUploadFolder.value = true;
uploadRef.value.$el.querySelector('input').click();
};
const removeFile = (index: number) => {
uploaderFiles.value.splice(index, 1);
};
const fileOnChange = (_uploadFile: UploadFile, uploadFiles: UploadFiles) => {
uploaderFiles.value = uploadFiles;
};
const clearFiles = () => {
uploadRef.value!.clearFiles();
};
const handleExceed: UploadProps['onExceed'] = (files) => {
uploadRef.value!.clearFiles();
for (let i = 0; i < files.length; i++) {
@ -111,7 +180,11 @@ const submit = async () => {
const formData = new FormData();
formData.append('filename', file.name);
if (file.raw.webkitRelativePath != '') {
formData.append('path', path.value + '/' + getPathWithoutFilename(file.raw.webkitRelativePath));
} else {
formData.append('path', path.value);
}
formData.append('chunk', chunk);
formData.append('chunkIndex', c.toString());
formData.append('chunkCount', chunkCount.toString());
@ -149,12 +222,58 @@ const submit = async () => {
}
};
const getPathWithoutFilename = (path: string) => {
return path ? path.split('/').slice(0, -1).join('/') : path;
};
const acceptParams = (props: UploadFileProps) => {
path.value = props.path;
open.value = true;
uploadPrecent.value = 0;
uploadHelper.value = '';
nextTick(() => {
const uploadEle = document.querySelector('.el-upload__input');
state.uploadEle = uploadEle;
});
};
defineExpose({ acceptParams });
</script>
<style lang="scss" scoped>
.button-container {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.file-item {
font-size: 12px;
color: #888;
position: relative;
display: flex;
align-items: center;
}
.file-item:hover {
background-color: #f5f5f5;
}
.file-icon {
margin-right: 8px;
}
.delete-button {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
}
.success-icon {
color: green;
position: absolute;
right: 0;
}
</style>