Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions agent/app/dto/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ type ImageInfo struct {
}

type ImageLoad struct {
TaskID string `json:"taskID"`
Path string `json:"path" validate:"required"`
TaskID string `json:"taskID"`
Paths []string `json:"paths" validate:"required,dive,required"`
}

type ImageBuild struct {
Expand Down
52 changes: 30 additions & 22 deletions agent/app/service/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,36 +321,44 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error {
}

func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
taskItem, err := task.NewTaskWithOps(req.Path, task.TaskImport, task.TaskScopeImage, req.TaskID, 1)
taskItem, err := task.NewTaskWithOps(strings.Join(req.Paths, ","), task.TaskImport, task.TaskScopeImage, req.TaskID, 1)
if err != nil {
return fmt.Errorf("new task for image import failed, err: %v", err)
}
taskItem.AddSubTask(i18n.GetWithName("TaskImport", req.Path), func(t *task.Task) error {
file, err := os.Open(req.Path)
if err != nil {
return err
}
defer file.Close()

go func() {
client, err := docker.NewDockerClient()
if err != nil {
return err
taskItem.Log("Failed to create Docker client: " + err.Error())
return
}
defer client.Close()
res, err := client.ImageLoad(context.TODO(), file)
if err != nil {
return err
}
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
return err
}
if strings.Contains(string(content), "Error") {
return errors.New(string(content))

for _, itemPath := range req.Paths {
currentPath := itemPath
itemName := path.Base(currentPath)
taskItem.AddSubTask(i18n.GetWithName("TaskImport", itemName), func(t *task.Task) error {
taskItem.Logf("----------------- %s -----------------", itemName)
file, err := os.Open(currentPath)
if err != nil {
return err
}
defer file.Close()
res, err := client.ImageLoad(context.TODO(), file)
if err != nil {
return err
}
defer res.Body.Close()
content, err := io.ReadAll(res.Body)
if err != nil {
return err
}
if strings.Contains(string(content), "Error") {
return errors.New(string(content))
}
return nil
}, nil)
}
return nil
}, nil)
go func() {
_ = taskItem.Execute()
}()
return nil
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/interface/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ export namespace Container {
}
export interface ImageLoad {
taskID: string;
path: string;
paths: Array<string>;
}
export interface ImageSave {
taskID: string;
Expand Down
66 changes: 57 additions & 9 deletions frontend/src/components/file-list/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
</el-table>
</div>
<div class="file-list-bottom">
<div v-if="selectRow?.path">
<div v-if="!isMultiple && selectRow?.path">
{{ $t('file.currentSelect') }}
<el-tooltip :content="selectRow.path" placement="top">
<el-tag type="success">
Expand All @@ -115,6 +115,23 @@
</el-tag>
</el-tooltip>
</div>
<div v-if="isMultiple && selectRows.length > 0">
{{ $t('file.currentSelect') }}
<el-tag
v-for="(item, index) in selectRows"
:key="item.path"
type="success"
closable
class="mr-1 mb-1"
@close="removeSelected(index)"
>
<el-tooltip :content="item.path" placement="top">
<div class="path">
<span>{{ item.path }}</span>
</div>
</el-tooltip>
</el-tag>
</div>
</div>

<template #footer>
Expand Down Expand Up @@ -142,6 +159,8 @@ const loading = ref(false);
const paths = ref<string[]>([]);
const req = reactive({ path: '/', expand: true, page: 1, pageSize: 300, showHidden: true });
const selectRow = ref({ path: '', name: '' });
const selectRows = ref<Array<{ path: string; name: string }>>([]);
const isMultiple = ref(false);
const rowRefs = ref();
const open = ref(false);
const newFolder = ref();
Expand All @@ -160,16 +179,20 @@ const form = reactive({
});

interface DialogProps {
path: string;
path?: string;
dir: boolean;
isAll: boolean;
disabled: boolean;
isAll?: boolean;
disabled?: boolean;
multiple?: boolean;
}
const acceptParams = (props: DialogProps): void => {
form.path = props.path || '/';
form.dir = props.dir;
form.isAll = props.isAll;
form.disabled = props.disabled;
form.isAll = props.isAll || false;
form.disabled = props.disabled || false;
isMultiple.value = props.multiple || false;
selectRows.value = [];
selectRow.value = { path: '', name: '' };
openPage();
req.path = form.path;
oldUrl.value = form.path;
Expand All @@ -178,15 +201,23 @@ const acceptParams = (props: DialogProps): void => {
};

const selectFile = () => {
if (selectRow.value) {
em('choose', selectRow.value.path);
if (isMultiple.value) {
em(
'choose',
selectRows.value.map((r) => r.path),
);
} else {
if (selectRow.value) {
em('choose', selectRow.value.path);
}
}
handleClose();
};

const handleClose = () => {
open.value = false;
selectRow.value = { path: '', name: '' };
selectRows.value = [];
};

const openPage = () => {
Expand Down Expand Up @@ -221,8 +252,21 @@ const openDir = async (row: File.File, column: any, event: any) => {
});
return;
}
// Handle file click
const fullPath = (req.path === '/' ? req.path : req.path + '/') + row.name;
if (isMultiple.value) {
// Multiple mode: toggle selection
const index = selectRows.value.findIndex((r) => r.path === fullPath);
if (index > -1) {
selectRows.value.splice(index, 1);
} else {
selectRows.value.push({ path: fullPath, name: row.name });
}
return;
}
// Single mode: original behavior
if (form.isAll || !form.dir) {
selectRow.value.path = (req.path === '/' ? req.path : req.path + '/') + row.name;
selectRow.value.path = fullPath;
return;
}
selectRow.value.path = '';
Expand Down Expand Up @@ -419,6 +463,10 @@ onUpdated(() => {
search(req);
});

const removeSelected = (index: number) => {
selectRows.value.splice(index, 1);
};

defineExpose({
acceptParams,
});
Expand Down
12 changes: 8 additions & 4 deletions frontend/src/views/container/image/load/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<el-form-item :label="$t('container.path')" :rules="Rules.requiredInput" prop="path">
<el-input v-model="form.path">
<template #prepend>
<el-button icon="Folder" @click="fileRef.acceptParams({ dir: false })" />
<el-button icon="Folder" @click="fileRef.acceptParams({ dir: false, multiple: true })" />
</template>
</el-input>
</el-form-item>
Expand Down Expand Up @@ -42,12 +42,14 @@ const taskLogRef = ref();
const loadVisible = ref(false);
const form = reactive({
path: '',
paths: [] as string[],
taskID: '',
});

const acceptParams = () => {
loadVisible.value = true;
form.path = '';
form.paths = [];
};
const handleClose = () => {
loadVisible.value = false;
Expand All @@ -64,7 +66,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!valid) return;
loading.value = true;
form.taskID = newUUID();
await imageLoad(form)
await imageLoad({ paths: form.paths, taskID: form.taskID })
.then(() => {
loading.value = false;
loadVisible.value = false;
Expand All @@ -82,8 +84,10 @@ const openTaskLog = (taskID: string) => {
taskLogRef.value.openWithTaskID(taskID);
};

const loadLoadDir = async (path: string) => {
form.path = path;
const loadLoadDir = async (paths: string | string[]) => {
const newPaths = Array.isArray(paths) ? paths : [paths];
form.paths = [...new Set([...form.paths, ...newPaths])];
form.path = form.paths.join('; ');
};

defineExpose({
Expand Down