mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-17 21:08:25 +08:00
fix: Fix the issue of multiple selection moving folder errors (#9317)
This commit is contained in:
parent
2bbba9d755
commit
8cc7d1f67f
14 changed files with 87 additions and 34 deletions
|
|
@ -92,11 +92,12 @@ type FileWget struct {
|
|||
}
|
||||
|
||||
type FileMove struct {
|
||||
Type string `json:"type" validate:"required"`
|
||||
OldPaths []string `json:"oldPaths" validate:"required"`
|
||||
NewPath string `json:"newPath" validate:"required"`
|
||||
Name string `json:"name"`
|
||||
Cover bool `json:"cover"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
OldPaths []string `json:"oldPaths" validate:"required"`
|
||||
NewPath string `json:"newPath" validate:"required"`
|
||||
Name string `json:"name"`
|
||||
Cover bool `json:"cover"`
|
||||
CoverPaths []string `json:"coverPaths"`
|
||||
}
|
||||
|
||||
type FileDownload struct {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ type ExistFileInfo struct {
|
|||
Path string `json:"path"`
|
||||
Size int64 `json:"size"`
|
||||
ModTime time.Time `json:"modTime"`
|
||||
IsDir bool `json:"isDir"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
|
|
|
|||
|
|
@ -394,10 +394,18 @@ func (f *FileService) MvFile(m request.FileMove) error {
|
|||
return buserr.New("ErrMovePathFailed")
|
||||
}
|
||||
}
|
||||
var errs []error
|
||||
if m.Type == "cut" {
|
||||
if len(m.CoverPaths) > 0 {
|
||||
for _, src := range m.CoverPaths {
|
||||
if err := fo.CopyAndReName(src, m.NewPath, "", true); err != nil {
|
||||
errs = append(errs, err)
|
||||
global.LOG.Errorf("cut copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
return fo.Cut(m.OldPaths, m.NewPath, m.Name, m.Cover)
|
||||
}
|
||||
var errs []error
|
||||
if m.Type == "copy" {
|
||||
for _, src := range m.OldPaths {
|
||||
if err := fo.CopyAndReName(src, m.NewPath, m.Name, m.Cover); err != nil {
|
||||
|
|
@ -569,6 +577,7 @@ func (f *FileService) BatchCheckFiles(req request.FilePathsCheck) []response.Exi
|
|||
Name: info.Name(),
|
||||
Path: filePath,
|
||||
ModTime: info.ModTime(),
|
||||
IsDir: info.IsDir(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -329,25 +329,30 @@ func (f FileOp) DownloadFile(url, dst string) error {
|
|||
}
|
||||
|
||||
func (f FileOp) Cut(oldPaths []string, dst, name string, cover bool) error {
|
||||
for _, p := range oldPaths {
|
||||
var dstPath string
|
||||
if name != "" {
|
||||
dstPath = filepath.Join(dst, name)
|
||||
if f.Stat(dstPath) {
|
||||
dstPath = dst
|
||||
}
|
||||
} else {
|
||||
base := filepath.Base(p)
|
||||
dstPath = filepath.Join(dst, base)
|
||||
if len(oldPaths) == 0 {
|
||||
return nil
|
||||
}
|
||||
var dstPath string
|
||||
coverFlag := ""
|
||||
if name != "" {
|
||||
dstPath = filepath.Join(dst, name)
|
||||
if f.Stat(dstPath) {
|
||||
dstPath = dst
|
||||
}
|
||||
coverFlag := ""
|
||||
if cover {
|
||||
coverFlag = "-f"
|
||||
}
|
||||
|
||||
if err := cmd.RunDefaultBashCf(`mv %s '%s' '%s'`, coverFlag, p, dstPath); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
dstPath = dst
|
||||
coverFlag = "-f"
|
||||
}
|
||||
var quotedPaths []string
|
||||
for _, p := range oldPaths {
|
||||
quotedPaths = append(quotedPaths, fmt.Sprintf("'%s'", p))
|
||||
}
|
||||
mvCommand := fmt.Sprintf("mv %s %s '%s'", coverFlag, strings.Join(quotedPaths, " "), dstPath)
|
||||
if err := cmd.RunDefaultBashCf(mvCommand); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ export namespace File {
|
|||
size: number;
|
||||
uploadSize: number;
|
||||
modTime: string;
|
||||
isDir: boolean;
|
||||
}
|
||||
|
||||
export interface RecycleBin {
|
||||
|
|
|
|||
|
|
@ -1467,7 +1467,8 @@ const message = {
|
|||
existFileTitle: 'Same name file prompt',
|
||||
existFileHelper: 'The uploaded file contains a file with the same name, do you want to overwrite it?',
|
||||
existFileSize: 'File size (new -> old)',
|
||||
existFileDirHelper: 'The selected file/folder has a duplicate name. Please proceed with caution!',
|
||||
existFileDirHelper: 'The selected file/folder has a duplicate name. Please proceed with caution! \n',
|
||||
coverDirHelper: 'The selected folders to be replaced will be copied to the destination path!',
|
||||
noSuchFile: 'The file or directory was not found. Please check and try again.',
|
||||
setting: 'Setting',
|
||||
showHide: 'Show hidden files',
|
||||
|
|
|
|||
|
|
@ -1411,6 +1411,7 @@ const message = {
|
|||
existFileHelper: 'アップロードしたファイルに同じ名前のファイルが含まれています。上書きしますか?',
|
||||
existFileSize: 'ファイルサイズ(新しい -> 古い)',
|
||||
existFileDirHelper: '選択したファイル/フォルダーには同じ名前のものが既に存在します。慎重に操作してください!',
|
||||
coverDirHelper: '上書きするフォルダを選択すると、対象パスにコピーされます!',
|
||||
noSuchFile: 'ファイルまたはディレクトリが見つかりませんでした。確認して再試行してください。',
|
||||
setting: '設定',
|
||||
showHide: '隠しファイルを表示',
|
||||
|
|
|
|||
|
|
@ -1397,6 +1397,7 @@ const message = {
|
|||
existFileHelper: '업로드한 파일에 동일한 이름의 파일이 포함되어 있습니다. 덮어쓰시겠습니까?',
|
||||
existFileSize: '파일 크기 (새로운 -> 오래된)',
|
||||
existFileDirHelper: '선택한 파일/폴더에 동일한 이름이 이미 존재합니다. 신중하게 작업하세요!',
|
||||
coverDirHelper: '덮어쓸 폴더를 선택하면 대상 경로로 복사됩니다!',
|
||||
noSuchFile: '파일 또는 디렉터리를 찾을 수 없습니다. 확인 후 다시 시도하세요.',
|
||||
setting: '설정',
|
||||
showHide: '숨김 파일 표시',
|
||||
|
|
|
|||
|
|
@ -1453,7 +1453,8 @@ const message = {
|
|||
existFileTitle: 'Amaran fail dengan nama yang sama',
|
||||
existFileHelper: 'Fail yang dimuat naik mengandungi fail dengan nama yang sama. Adakah anda mahu menimpanya?',
|
||||
existFileSize: 'Saiz fail (baru -> lama)',
|
||||
existFileDirHelper: 'Fail/folder yang dipilih mempunyai nama yang sama. Sila berhati-hati!',
|
||||
existFileDirHelper: 'Fail/folder yang dipilih mempunyai nama yang sama. Sila berhati-hati!\n',
|
||||
coverDirHelper: 'Folder yang dipilih untuk ditimpa akan disalin ke laluan destinasi!',
|
||||
noSuchFile: 'Fail atau direktori tidak ditemui. Sila periksa dan cuba lagi.',
|
||||
setting: 'tetapan',
|
||||
showHide: 'Tunjukkan fail tersembunyi',
|
||||
|
|
|
|||
|
|
@ -1439,7 +1439,8 @@ const message = {
|
|||
existFileTitle: 'Aviso de arquivo com o mesmo nome',
|
||||
existFileHelper: 'O arquivo enviado contém um arquivo com o mesmo nome. Deseja substituí-lo?',
|
||||
existFileSize: 'Tamanho do arquivo (novo -> antigo)',
|
||||
existFileDirHelper: 'O arquivo/pasta selecionado tem um nome duplicado. Por favor, prossiga com cautela!',
|
||||
existFileDirHelper: 'O arquivo/pasta selecionado tem um nome duplicado. Por favor, prossiga com cautela!\n',
|
||||
coverDirHelper: 'As pastas selecionadas para substituição serão copiadas para o caminho de destino!',
|
||||
noSuchFile: 'O arquivo ou diretório não foi encontrado. Por favor, verifique e tente novamente.',
|
||||
setting: 'configuração',
|
||||
showHide: 'Mostrar arquivos ocultos',
|
||||
|
|
|
|||
|
|
@ -1440,7 +1440,8 @@ const message = {
|
|||
existFileTitle: 'Предупреждение о файле с тем же именем',
|
||||
existFileHelper: 'Загруженный файл содержит файл с таким же именем. Заменить его?',
|
||||
existFileSize: 'Размер файла (новый -> старый)',
|
||||
existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!',
|
||||
existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!\n',
|
||||
coverDirHelper: 'При выборе перезаписываемой папки она будет скопирована в целевой путь!',
|
||||
noSuchFile: 'Файл или каталог не найдены. Пожалуйста, проверьте и повторите попытку.',
|
||||
setting: 'настройка',
|
||||
showHide: 'Показывать скрытые файлы',
|
||||
|
|
|
|||
|
|
@ -1401,6 +1401,7 @@ const message = {
|
|||
existFileHelper: '上傳的檔案存在同名檔案,是否覆蓋?',
|
||||
existFileSize: '文件大小(新->舊)',
|
||||
existFileDirHelper: '選擇的檔案/資料夾存在同名,請謹慎操作!',
|
||||
coverDirHelper: '選取要覆蓋的資料夾,將複製到目標路徑!',
|
||||
noSuchFile: '找不到該檔案或目錄,請檢查後重試。',
|
||||
setting: '设置',
|
||||
showHide: '顯示隱藏檔案',
|
||||
|
|
|
|||
|
|
@ -1395,6 +1395,7 @@ const message = {
|
|||
existFileHelper: '上传的文件存在同名文件,是否覆盖?',
|
||||
existFileSize: '文件大小 (新 -> 旧)',
|
||||
existFileDirHelper: '选择的文件/文件夹存在同名,请谨慎操作!',
|
||||
coverDirHelper: '选中覆盖的文件夹,将复制到目标路径!',
|
||||
noSuchFile: '未能找到该文件或目录,请检查后重试',
|
||||
setting: '设置',
|
||||
showHide: '显示隐藏文件',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<DrawerPro v-model="open" :header="title" @close="handleClose" size="675">
|
||||
<DrawerPro v-model="open" :header="title" @close="handleClose" size="690">
|
||||
<el-form
|
||||
@submit.prevent
|
||||
ref="fileForm"
|
||||
|
|
@ -8,6 +8,16 @@
|
|||
:rules="rules"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-alert
|
||||
v-if="type == 'cut' && existFiles?.length == 0 && addForm.cover && changeName"
|
||||
show-icon
|
||||
type="warning"
|
||||
:closable="false"
|
||||
>
|
||||
<template #title>
|
||||
<span class="whitespace-break-spaces">{{ $t('file.coverDirHelper') }}</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-form-item :label="$t('file.path')" prop="newPath">
|
||||
<el-input v-model="addForm.newPath">
|
||||
<template #prepend><FileList @choose="getPath" :dir="true"></FileList></template>
|
||||
|
|
@ -23,10 +33,12 @@
|
|||
</el-radio-group>
|
||||
</div>
|
||||
<div v-if="existFiles.length > 0 && !changeName" class="text-center">
|
||||
<el-alert :show-icon="true" type="warning" :closable="false">
|
||||
<div class="whitespace-break-spaces">
|
||||
<span>{{ $t('file.existFileDirHelper') }}</span>
|
||||
</div>
|
||||
<el-alert show-icon type="warning" :closable="false">
|
||||
<template #title>
|
||||
<span class="whitespace-break-spaces">
|
||||
{{ $t('file.existFileDirHelper') + $t('file.coverDirHelper') }}
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-transfer
|
||||
v-model="skipFiles"
|
||||
|
|
@ -60,6 +72,7 @@ import { ref, reactive, computed, ComputedRef } from 'vue';
|
|||
import FileList from '@/components/file-list/index.vue';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { getDateStr } from '@/utils/util';
|
||||
import { File } from '@/api/interface/file';
|
||||
|
||||
interface MoveProps {
|
||||
oldPaths: Array<string>;
|
||||
|
|
@ -76,7 +89,7 @@ const open = ref(false);
|
|||
const type = ref('cut');
|
||||
const changeName = ref(false);
|
||||
const oldName = ref('');
|
||||
const existFiles = ref([]);
|
||||
const existFiles = ref<File.ExistFileInfo[]>([]);
|
||||
const skipFiles = ref([]);
|
||||
const transferData = ref([]);
|
||||
|
||||
|
|
@ -96,6 +109,7 @@ const addForm = reactive({
|
|||
allNames: [] as string[],
|
||||
isDir: false,
|
||||
cover: false,
|
||||
coverPaths: [] as string[],
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules>({
|
||||
|
|
@ -122,7 +136,20 @@ const getFileName = (filePath: string) => {
|
|||
};
|
||||
|
||||
const coverFiles: ComputedRef<string[]> = computed(() => {
|
||||
return addForm.oldPaths.filter((item) => !skipFiles.value.includes(getFileName(item))).map((item) => item);
|
||||
const existingNames = new Set(
|
||||
existFiles.value.filter((item) => !skipFiles.value.includes(item.name) && item.isDir).map((item) => item.name),
|
||||
);
|
||||
return addForm.oldPaths.filter((path) => existingNames.has(getFileName(path)));
|
||||
});
|
||||
|
||||
const mvFiles: ComputedRef<string[]> = computed(() => {
|
||||
const skipSet = new Set(skipFiles.value);
|
||||
const coverSet = new Set(coverFiles.value.map(getFileName));
|
||||
|
||||
return addForm.oldPaths.filter((path) => {
|
||||
const name = getFileName(path);
|
||||
return !skipSet.has(name) && !coverSet.has(name);
|
||||
});
|
||||
});
|
||||
|
||||
const getPath = (path: string) => {
|
||||
|
|
@ -159,7 +186,8 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
return;
|
||||
}
|
||||
loading.value = true;
|
||||
addForm.oldPaths = coverFiles.value;
|
||||
addForm.coverPaths = coverFiles.value;
|
||||
addForm.oldPaths = mvFiles.value;
|
||||
mvFile();
|
||||
});
|
||||
};
|
||||
|
|
@ -247,7 +275,7 @@ defineExpose({ acceptParams });
|
|||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-transfer) {
|
||||
--el-transfer-panel-width: 250px;
|
||||
--el-transfer-panel-width: 260px;
|
||||
.el-button {
|
||||
padding: 4px 7px;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue