From 4d279a521e6e434584da18ad1d122869bcce0490 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Fri, 15 Dec 2023 16:22:08 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=95=9C=E5=83=8F=20Tag=20=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=BF=AE=E6=94=B9=20(#3346)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #3316 --- backend/app/dto/image.go | 1 - cmd/server/docs/docs.go | 6 +- cmd/server/docs/swagger.json | 6 +- cmd/server/docs/swagger.yaml | 4 +- frontend/src/api/interface/container.ts | 1 - frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/tw.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + .../container/container/upgrade/index.vue | 2 + .../views/container/image/delete/index.vue | 4 +- frontend/src/views/container/image/index.vue | 4 +- .../src/views/container/image/tag/index.vue | 90 ++++++++++++------- .../src/views/toolbox/device/swap/index.vue | 2 +- 13 files changed, 77 insertions(+), 46 deletions(-) diff --git a/backend/app/dto/image.go b/backend/app/dto/image.go index 892e0a24f..541c5ffb0 100644 --- a/backend/app/dto/image.go +++ b/backend/app/dto/image.go @@ -27,7 +27,6 @@ type ImagePull struct { } type ImageTag struct { - RepoID uint `json:"repoID"` SourceID string `json:"sourceID" validate:"required"` TargetName string `json:"targetName" validate:"required"` } diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index f46955f50..b0aa252fc 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -15460,9 +15460,6 @@ const docTemplate = `{ "targetName" ], "properties": { - "repoID": { - "type": "integer" - }, "sourceID": { "type": "string" }, @@ -18301,6 +18298,9 @@ const docTemplate = `{ "url" ], "properties": { + "ignoreCertificate": { + "type": "boolean" + }, "name": { "type": "string" }, diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 2077d314f..0a120cffe 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -15453,9 +15453,6 @@ "targetName" ], "properties": { - "repoID": { - "type": "integer" - }, "sourceID": { "type": "string" }, @@ -18294,6 +18291,9 @@ "url" ], "properties": { + "ignoreCertificate": { + "type": "boolean" + }, "name": { "type": "string" }, diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 0b1a6146b..053b975ec 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -1398,8 +1398,6 @@ definitions: type: object dto.ImageTag: properties: - repoID: - type: integer sourceID: type: string targetName: @@ -3300,6 +3298,8 @@ definitions: type: object request.FileWget: properties: + ignoreCertificate: + type: boolean name: type: string path: diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index e348c3ddd..baef4e432 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -133,7 +133,6 @@ export namespace Container { imageName: string; } export interface ImageTag { - repoID: number; sourceID: string; targetName: string; } diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 705ec230b..842d97593 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -635,6 +635,7 @@ const message = { imagePush: 'Image push', imageDelete: 'Image delete', imageDeleteTag: 'Image tag delete', + imageTagDeleteHelper: 'Remove other tags associated with this image ID', repoName: 'Repo Name', imageName: 'Image name', pull: 'Pull', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 71d871de6..dd87f3162 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -617,6 +617,7 @@ const message = { imagePush: '推送鏡像', imageDelete: '刪除鏡像', imageDeleteTag: '刪除 Tag', + imageTagDeleteHelper: '移除與該映像 ID 相關聯的其他標籤', repoName: '倉庫名', imageName: '鏡像名', httpRepo: 'http 倉庫添加授信需要重啟 docker 服務', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 09090b1de..30d296f48 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -618,6 +618,7 @@ const message = { imagePush: '推送镜像', imageDelete: '删除镜像', imageDeleteTag: '删除 Tag', + imageTagDeleteHelper: '移除与该镜像 ID 相关联的其他标签', repoName: '仓库名', imageName: '镜像名', httpRepo: 'http 仓库添加授信需要重启 docker 服务', diff --git a/frontend/src/views/container/container/upgrade/index.vue b/frontend/src/views/container/container/upgrade/index.vue index e44d52ed9..aa5a01230 100644 --- a/frontend/src/views/container/container/upgrade/index.vue +++ b/frontend/src/views/container/container/upgrade/index.vue @@ -94,6 +94,8 @@ const acceptParams = (props: DialogProps): void => { form.hasName = props.image.indexOf('sha256:') === -1; if (form.hasName) { form.newImageName = props.image; + } else { + form.newImageName = ''; } drawerVisible.value = true; }; diff --git a/frontend/src/views/container/image/delete/index.vue b/frontend/src/views/container/image/delete/index.vue index 870d73f6a..f545e5962 100644 --- a/frontend/src/views/container/image/delete/index.vue +++ b/frontend/src/views/container/image/delete/index.vue @@ -17,7 +17,7 @@ {{ $t('container.removeAll') }} </el-checkbox> </div> - <el-checkbox-group v-model="form.deleteTags" @change="handleCheckedCitiesChange"> + <el-checkbox-group v-model="form.deleteTags" @change="handleCheckedChange"> <div> <el-checkbox style="width: 100%" @@ -91,7 +91,7 @@ const handleCheckAllChange = (val: boolean) => { form.deleteTags = val ? form.tags : []; isIndeterminate.value = false; }; -const handleCheckedCitiesChange = (value: string[]) => { +const handleCheckedChange = (value: string[]) => { const checkedCount = value.length; deleteAll.value = checkedCount === form.tags.length; isIndeterminate.value = checkedCount > 0 && checkedCount < form.tags.length; diff --git a/frontend/src/views/container/image/index.vue b/frontend/src/views/container/image/index.vue index a0f50ff8a..39ba966bb 100644 --- a/frontend/src/views/container/image/index.vue +++ b/frontend/src/views/container/image/index.vue @@ -243,9 +243,9 @@ const buttons = [ label: i18n.global.t('container.tag'), click: (row: Container.ImageInfo) => { let params = { - itemName: row.tags && row.tags?.length !== 0 ? row.tags[0].split(':')[0] : '', repos: repos.value, - sourceID: row.id, + imageID: row.id, + tags: row.tags, }; dialogTagRef.value!.acceptParams(params); }, diff --git a/frontend/src/views/container/image/tag/index.vue b/frontend/src/views/container/image/tag/index.vue index 3f5ea035e..f89b5b114 100644 --- a/frontend/src/views/container/image/tag/index.vue +++ b/frontend/src/views/container/image/tag/index.vue @@ -1,7 +1,7 @@ <template> - <el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="30%"> + <el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%"> <template #header> - <DrawerHeader :header="$t('container.imageTag')" :resource="form.itemName" :back="handleClose" /> + <DrawerHeader :header="$t('container.imageTag')" :back="handleClose" /> </template> <el-form v-loading="loading" label-position="top" ref="formRef" :model="form" label-width="80px"> <el-row type="flex" justify="center"> @@ -13,16 +13,29 @@ v-if="form.fromRepo" :label="$t('container.repoName')" :rules="Rules.requiredSelect" - prop="repoID" + prop="repo" > - <el-select style="width: 100%" filterable v-model="form.repoID"> - <el-option v-for="item in repos" :key="item.id" :value="item.id" :label="item.name" /> + <el-select style="width: 100%" filterable v-model="form.repo" @change="changeRepo"> + <el-option v-for="item in repos" :key="item.id" :value="item.name" :label="item.name" /> </el-select> </el-form-item> - <el-form-item :label="$t('container.imageName')" :rules="Rules.imageName" prop="targetName"> - <el-input v-model="form.targetName"> - <template v-if="form.fromRepo" #prepend>{{ loadDetailInfo(form.repoID) }}/</template> - </el-input> + <el-form-item :label="$t('container.imageTag')" :rules="Rules.imageName" prop="targetName"> + <el-input v-model="form.targetName" /> + </el-form-item> + + <el-form-item> + <el-checkbox style="width: 100%" v-model="form.deleteTag"> + {{ $t('container.imageTagDeleteHelper') }} + </el-checkbox> + <el-checkbox-group class="ml-5" v-if="form.deleteTag" v-model="form.deleteTags"> + <el-checkbox + style="width: 100%" + v-for="item in tags" + :key="item" + :value="item" + :label="item" + /> + </el-checkbox-group> </el-form-item> </el-col> </el-row> @@ -46,7 +59,7 @@ import { reactive, ref } from 'vue'; import { Rules } from '@/global/form-rules'; import i18n from '@/lang'; import { ElForm } from 'element-plus'; -import { imageTag } from '@/api/modules/container'; +import { imageRemove, imageTag } from '@/api/modules/container'; import { Container } from '@/api/interface/container'; import DrawerHeader from '@/components/drawer-header/index.vue'; import { MsgSuccess } from '@/utils/message'; @@ -55,28 +68,35 @@ const loading = ref(false); const drawerVisible = ref(false); const repos = ref(); +const tags = ref(); const form = reactive({ - itemName: '', - sourceID: '', - fromRepo: true, - repoID: 1, + imageID: '', + fromRepo: false, + repo: '', + originName: '', targetName: '', + + deleteTag: false, + deleteTags: [], }); interface DialogProps { - itemName: string; repos: Array<Container.RepoOptions>; - sourceID: string; + imageID: string; + tags: Array<string>; } const acceptParams = async (params: DialogProps): Promise<void> => { drawerVisible.value = true; - form.repoID = 1; - form.itemName = params.itemName; - form.sourceID = params.sourceID; - form.targetName = ''; - form.fromRepo = true; + form.imageID = params.imageID; + form.originName = params.tags?.length !== 0 ? params.tags[0] : ''; + form.targetName = params.tags?.length !== 0 ? params.tags[0] : ''; + form.fromRepo = false; + form.repo = ''; + form.deleteTag = false; + form.deleteTags = []; repos.value = params.repos; + tags.value = params.tags; }; const emit = defineEmits<{ (e: 'search'): void }>(); @@ -91,13 +111,17 @@ const onSubmit = async (formEl: FormInstance | undefined) => { if (!formEl) return; formEl.validate(async (valid) => { if (!valid) return; - if (!form.fromRepo) { - form.repoID = 0; - } + let params = { + sourceID: form.imageID, + targetName: form.targetName, + }; loading.value = true; - await imageTag(form) - .then(() => { + await imageTag(params) + .then(async () => { loading.value = false; + if (form.deleteTag && form.deleteTags.length !== 0) { + await imageRemove({ names: form.deleteTags }); + } drawerVisible.value = false; emit('search'); MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); @@ -108,14 +132,18 @@ const onSubmit = async (formEl: FormInstance | undefined) => { }); }; -function loadDetailInfo(id: number) { +const changeRepo = (val) => { + if (val === 'Docker Hub') { + form.targetName = form.originName; + return; + } for (const item of repos.value) { - if (item.id === id) { - return item.downloadUrl; + if (item.name == val) { + form.targetName = item.downloadUrl + '/' + form.originName; + return; } } - return ''; -} +}; defineExpose({ acceptParams, diff --git a/frontend/src/views/toolbox/device/swap/index.vue b/frontend/src/views/toolbox/device/swap/index.vue index 56927d8ff..48a8061dd 100644 --- a/frontend/src/views/toolbox/device/swap/index.vue +++ b/frontend/src/views/toolbox/device/swap/index.vue @@ -147,7 +147,7 @@ const loadData = (path: string) => { item.size = itemSize.size; item.sizeUnit = itemSize.unit; } - if (!isExist && path !== '') { + if (!isExist) { form.swapDetails.push({ path: path + '/.1panel_swap', size: 0,