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

feat: 镜像删除增加存在性判断 (#3114)

Refs #3109
This commit is contained in:
ssongliu 2023-11-30 14:14:08 +08:00 committed by GitHub
parent ca45edae5f
commit 833efb0136
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 12 deletions

View File

@ -168,6 +168,7 @@ type VolumeCreate struct {
}
type BatchDelete struct {
Force bool `json:"force"`
Names []string `json:"names" validate:"required"`
}

View File

@ -390,7 +390,7 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
return err
}
for _, id := range req.Names {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: true, PruneChildren: true}); err != nil {
if _, err := client.ImageRemove(context.TODO(), id, types.ImageRemoveOptions{Force: req.Force, PruneChildren: true}); err != nil {
if strings.Contains(err.Error(), "image is being used") {
if strings.Contains(id, "sha256:") {
return buserr.New(constant.ErrObjectInUsed)

View File

@ -524,6 +524,7 @@ const message = {
unpause: 'Unpause',
rename: 'Rename',
remove: 'Remove',
removeAll: 'Remove All',
containerPrune: 'Container prune',
containerPruneHelper1: 'Cleaning containers will delete all containers that are in a stopped state.',
containerPruneHelper2:

View File

@ -512,6 +512,7 @@ const message = {
unpause: '恢復',
rename: '重命名',
remove: '刪除',
removeAll: '删除所有',
containerPrune: '清理容器',
containerPruneHelper1: '清理容器 將刪除所有處於停止狀態的容器',
containerPruneHelper2:

View File

@ -513,6 +513,7 @@ const message = {
unpause: '恢复',
rename: '重命名',
remove: '删除',
removeAll: '删除所有',
containerPrune: '清理容器',
containerPruneHelper1: '清理容器 将删除所有处于停止状态的容器',
containerPruneHelper2:

View File

@ -4,15 +4,24 @@
<template #header>
<DrawerHeader :header="$t('container.imageDelete')" :back="handleClose" />
</template>
<el-form @submit.prevent :model="deleteForm" label-position="top">
<el-form @submit.prevent :model="form" label-position="top">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item :label="$t('container.tag')" prop="tagName">
<el-checkbox-group v-model="deleteForm.deleteTags">
<div style="width: 100%">
<el-checkbox
v-model="deleteAll"
:indeterminate="isIndeterminate"
@change="handleCheckAllChange"
>
{{ $t('container.removeAll') }}
</el-checkbox>
</div>
<el-checkbox-group v-model="form.deleteTags" @change="handleCheckedCitiesChange">
<div>
<el-checkbox
style="width: 100%"
v-for="item in deleteForm.tags"
v-for="item in form.tags"
:key="item"
:value="item"
:label="item"
@ -26,7 +35,7 @@
<template #footer>
<span class="dialog-footer">
<el-button @click="deleteVisible = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" :disabled="deleteForm.deleteTags.length === 0" @click="batchDelete()">
<el-button type="primary" :disabled="form.deleteTags.length === 0" @click="batchDelete()">
{{ $t('commons.button.delete') }}
</el-button>
</span>
@ -45,20 +54,29 @@ import DrawerHeader from '@/components/drawer-header/index.vue';
import i18n from '@/lang';
const deleteVisible = ref(false);
const deleteForm = reactive({
const form = reactive({
id: '',
force: false,
tags: [] as Array<string>,
deleteTags: [] as Array<string>,
});
const deleteAll = ref();
const isIndeterminate = ref(true);
const opRef = ref();
interface DialogProps {
id: string;
isUsed: boolean;
tags: Array<string>;
}
const acceptParams = (params: DialogProps) => {
deleteAll.value = false;
deleteVisible.value = true;
deleteForm.deleteTags = [];
deleteForm.tags = params.tags;
form.deleteTags = [];
form.id = params.id;
form.tags = params.tags;
form.force = !params.isUsed;
};
const handleClose = () => {
deleteVisible.value = false;
@ -69,20 +87,39 @@ const onSearch = () => {
emit('search');
};
const handleCheckAllChange = (val: boolean) => {
form.deleteTags = val ? form.tags : [];
isIndeterminate.value = false;
};
const handleCheckedCitiesChange = (value: string[]) => {
const checkedCount = value.length;
deleteAll.value = checkedCount === form.tags.length;
isIndeterminate.value = checkedCount > 0 && checkedCount < form.tags.length;
};
const batchDelete = async () => {
let names = [];
for (const item of deleteForm.deleteTags) {
names.push(item);
let showNames = [];
if (deleteAll.value) {
names.push(form.id);
for (const item of form.deleteTags) {
showNames.push(item);
}
} else {
for (const item of form.deleteTags) {
names.push(item);
showNames.push(item);
}
}
opRef.value.acceptParams({
title: i18n.global.t('commons.button.delete'),
names: names,
names: showNames,
msg: i18n.global.t('commons.msg.operatorHelper', [
i18n.global.t('container.image'),
i18n.global.t('commons.button.delete'),
]),
api: imageRemove,
params: { names: names },
params: { names: names, force: form.force },
});
};

View File

@ -279,6 +279,7 @@ const buttons = [
}
let params = {
id: row.id,
isUsed: row.isUsed,
tags: row.tags,
};
dialogDeleteRef.value!.acceptParams(params);