mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-02 11:34:24 +08:00
fix: Fix the issue of multiple selection moving folder errors (#9319)
Some checks failed
SonarCloud Scan / SonarCloud (push) Failing after 5s
Some checks failed
SonarCloud Scan / SonarCloud (push) Failing after 5s
refs: #8612
This commit is contained in:
parent
21bbadc37b
commit
6e465ac215
13 changed files with 87 additions and 35 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,4 +53,5 @@ type ExistFileInfo struct {
|
|||
Path string `json:"path"`
|
||||
Size float64 `json:"size"`
|
||||
ModTime time.Time `json:"modTime"`
|
||||
IsDir bool `json:"isDir"`
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -1364,6 +1364,7 @@ const message = {
|
|||
existFileHelper: 'アップロードしたファイルに同じ名前のファイルが含まれています。上書きしますか?',
|
||||
existFileSize: 'ファイルサイズ(新しい -> 古い)',
|
||||
existFileDirHelper: '選択したファイル/フォルダーには同じ名前のものが既に存在します。慎重に操作してください!',
|
||||
coverDirHelper: '上書きするフォルダを選択すると、対象パスにコピーされます!',
|
||||
noSuchFile: 'ファイルまたはディレクトリが見つかりませんでした。確認して再試行してください。',
|
||||
},
|
||||
ssh: {
|
||||
|
|
|
@ -1350,6 +1350,7 @@ const message = {
|
|||
existFileHelper: '업로드한 파일에 동일한 이름의 파일이 포함되어 있습니다. 덮어쓰시겠습니까?',
|
||||
existFileSize: '파일 크기 (새로운 -> 오래된)',
|
||||
existFileDirHelper: '선택한 파일/폴더에 동일한 이름이 이미 존재합니다. 신중하게 작업하세요!',
|
||||
coverDirHelper: '덮어쓸 폴더를 선택하면 대상 경로로 복사됩니다!',
|
||||
noSuchFile: '파일 또는 디렉터리를 찾을 수 없습니다. 확인 후 다시 시도하세요.',
|
||||
},
|
||||
ssh: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -1395,7 +1395,8 @@ const message = {
|
|||
existFileTitle: 'Предупреждение о файле с тем же именем',
|
||||
existFileHelper: 'Загруженный файл содержит файл с таким же именем. Заменить его?',
|
||||
existFileSize: 'Размер файла (новый -> старый)',
|
||||
existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!',
|
||||
existFileDirHelper: 'Выбранный файл/папка имеет дублирующееся имя. Пожалуйста, действуйте осторожно!\n',
|
||||
coverDirHelper: 'При выборе перезаписываемой папки она будет скопирована в целевой путь!',
|
||||
noSuchFile: 'Файл или каталог не найдены. Пожалуйста, проверьте и повторите попытку.',
|
||||
},
|
||||
ssh: {
|
||||
|
|
|
@ -1320,6 +1320,7 @@ const message = {
|
|||
existFileHelper: '上傳的檔案存在同名檔案,是否覆蓋?',
|
||||
existFileSize: '文件大小(新->舊)',
|
||||
existFileDirHelper: '選擇的檔案/資料夾存在同名,請謹慎操作!',
|
||||
coverDirHelper: '選取要覆蓋的資料夾,將複製到目標路徑!',
|
||||
noSuchFile: '找不到該檔案或目錄,請檢查後重試。',
|
||||
},
|
||||
ssh: {
|
||||
|
|
|
@ -1322,6 +1322,7 @@ const message = {
|
|||
existFileHelper: '上传的文件存在同名文件,是否覆盖?',
|
||||
existFileSize: '文件大小 (新 -> 旧)',
|
||||
existFileDirHelper: '选择的文件/文件夹存在同名,请谨慎操作!',
|
||||
coverDirHelper: '选中覆盖的文件夹,将复制到目标路径!',
|
||||
noSuchFile: '未能找到该文件或目录,请检查后重试',
|
||||
},
|
||||
ssh: {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue