fix: Fix the issue of multiple selection moving folder errors (#9319)
Some checks failed
SonarCloud Scan / SonarCloud (push) Failing after 5s

refs: #8612
This commit is contained in:
2025-06-27 14:03:24 +08:00 committed by GitHub
parent 21bbadc37b
commit 6e465ac215
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 87 additions and 35 deletions

View file

@ -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 {

View file

@ -53,4 +53,5 @@ type ExistFileInfo struct {
Path string `json:"path"`
Size float64 `json:"size"`
ModTime time.Time `json:"modTime"`
IsDir bool `json:"isDir"`
}

View file

@ -366,10 +366,18 @@ func (f *FileService) MvFile(m request.FileMove) error {
return buserr.New(constant.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 {
@ -518,6 +526,7 @@ func (f *FileService) BatchCheckFiles(req request.FilePathsCheck) []response.Exi
Name: info.Name(),
Path: filePath,
ModTime: info.ModTime(),
IsDir: info.IsDir(),
})
}
}

View file

@ -349,26 +349,30 @@ func (f FileOp) DownloadFileWithProxy(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"
}
cmdStr := fmt.Sprintf(`mv %s '%s' '%s'`, coverFlag, p, dstPath)
if err := cmd.ExecCmd(cmdStr); 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.ExecCmd(mvCommand); err != nil {
return err
}
return nil
}

View file

@ -1385,7 +1385,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.',
},
ssh: {

View file

@ -1364,6 +1364,7 @@ const message = {
existFileHelper: 'アップロードしたファイルに同じ名前のファイルが含まれています上書きしますか',
existFileSize: 'ファイルサイズ新しい -> 古い',
existFileDirHelper: '選択したファイル/フォルダーには同じ名前のものが既に存在します慎重に操作してください',
coverDirHelper: '上書きするフォルダを選択すると対象パスにコピーされます',
noSuchFile: 'ファイルまたはディレクトリが見つかりませんでした確認して再試行してください',
},
ssh: {

View file

@ -1350,6 +1350,7 @@ const message = {
existFileHelper: '업로드한 파일에 동일한 이름의 파일이 포함되어 있습니다. 덮어쓰시겠습니까?',
existFileSize: '파일 크기 (새로운 -> 오래된)',
existFileDirHelper: '선택한 파일/폴더에 동일한 이름이 이미 존재합니다. 신중하게 작업하세요!',
coverDirHelper: '덮어쓸 폴더를 선택하면 대상 경로로 복사됩니다!',
noSuchFile: '파일 또는 디렉터리를 찾을 없습니다. 확인 다시 시도하세요.',
},
ssh: {

View file

@ -1406,7 +1406,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.',
},
ssh: {

View file

@ -1393,7 +1393,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.',
},
ssh: {

View file

@ -1395,7 +1395,8 @@ const message = {
existFileTitle: 'Предупреждение о файле с тем же именем',
existFileHelper: 'Загруженный файл содержит файл с таким же именем. Заменить его?',
existFileSize: 'Размер файла (новый -> старый)',
existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!',
existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!\n',
coverDirHelper: 'При выборе перезаписываемой папки она будет скопирована в целевой путь!',
noSuchFile: 'Файл или каталог не найдены. Пожалуйста, проверьте и повторите попытку.',
},
ssh: {

View file

@ -1320,6 +1320,7 @@ const message = {
existFileHelper: '上傳的檔案存在同名檔案是否覆蓋',
existFileSize: '文件大小->',
existFileDirHelper: '選擇的檔案/資料夾存在同名請謹慎操作',
coverDirHelper: '選取要覆蓋的資料夾將複製到目標路徑',
noSuchFile: '找不到該檔案或目錄請檢查後重試',
},
ssh: {

View file

@ -1322,6 +1322,7 @@ const message = {
existFileHelper: '上传的文件存在同名文件是否覆盖',
existFileSize: '文件大小 ( -> )',
existFileDirHelper: '选择的文件/文件夹存在同名请谨慎操作',
coverDirHelper: '选中覆盖的文件夹将复制到目标路径',
noSuchFile: '未能找到该文件或目录请检查后重试',
},
ssh: {

View file

@ -4,7 +4,7 @@
:destroy-on-close="true"
:close-on-click-modal="false"
:close-on-press-escape="false"
size="675"
size="690"
>
<template #header>
<DrawerHeader :header="title" :back="handleClose" />
@ -19,6 +19,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>
@ -34,10 +44,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"
@ -74,6 +86,7 @@ import FileList from '@/components/file-list/index.vue';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message';
import { getDateStr } from '@/utils/util';
import { File } from '@/api/interface/file';
interface MoveProps {
oldPaths: Array<string>;
@ -90,7 +103,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([]);
@ -110,6 +123,7 @@ const addForm = reactive({
allNames: [] as string[],
isDir: false,
cover: false,
coverPaths: [] as string[],
});
const rules = reactive<FormRules>({
@ -136,7 +150,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) => {
@ -173,7 +200,8 @@ const submit = async (formEl: FormInstance | undefined) => {
return;
}
loading.value = true;
addForm.oldPaths = coverFiles.value;
addForm.coverPaths = coverFiles.value;
addForm.oldPaths = mvFiles.value;
mvFile();
});
};
@ -232,6 +260,7 @@ const acceptParams = async (props: MoveProps) => {
addForm.name = '';
addForm.allNames = props.allNames;
type.value = props.type;
existFiles.value = [];
if (props.name && props.name != '') {
oldName.value = props.name;
const res = await CheckFile(props.path + '/' + props.name, false);
@ -261,7 +290,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;
}