feat: 镜像 Tag 逻辑修改 (#3346)

Refs #3316
This commit is contained in:
ssongliu 2023-12-15 16:22:08 +08:00 committed by GitHub
parent 3fb5e523a8
commit 4d279a521e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 77 additions and 46 deletions

View file

@ -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"`
}

View file

@ -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"
},

View file

@ -15453,9 +15453,6 @@
"targetName"
],
"properties": {
"repoID": {
"type": "integer"
},
"sourceID": {
"type": "string"
},
@ -18294,6 +18291,9 @@
"url"
],
"properties": {
"ignoreCertificate": {
"type": "boolean"
},
"name": {
"type": "string"
},

View file

@ -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:

View file

@ -133,7 +133,6 @@ export namespace Container {
imageName: string;
}
export interface ImageTag {
repoID: number;
sourceID: string;
targetName: string;
}

View file

@ -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',

View file

@ -617,6 +617,7 @@ const message = {
imagePush: '推送鏡像',
imageDelete: '刪除鏡像',
imageDeleteTag: '刪除 Tag',
imageTagDeleteHelper: '移除與該映像 ID 相關聯的其他標籤',
repoName: '倉庫名',
imageName: '鏡像名',
httpRepo: 'http 倉庫添加授信需要重啟 docker 服務',

View file

@ -618,6 +618,7 @@ const message = {
imagePush: '推送镜像',
imageDelete: '删除镜像',
imageDeleteTag: '删除 Tag',
imageTagDeleteHelper: '移除与该镜像 ID 相关联的其他标签',
repoName: '仓库名',
imageName: '镜像名',
httpRepo: 'http 仓库添加授信需要重启 docker 服务',

View file

@ -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;
};

View file

@ -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;

View file

@ -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);
},

View file

@ -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,

View file

@ -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,