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 { type BatchDelete struct {
Force bool `json:"force"`
Names []string `json:"names" validate:"required"` Names []string `json:"names" validate:"required"`
} }

View File

@ -390,7 +390,7 @@ func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
return err return err
} }
for _, id := range req.Names { 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(err.Error(), "image is being used") {
if strings.Contains(id, "sha256:") { if strings.Contains(id, "sha256:") {
return buserr.New(constant.ErrObjectInUsed) return buserr.New(constant.ErrObjectInUsed)

View File

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

View File

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

View File

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

View File

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

View File

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