mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-09-22 16:00:51 +00:00
feat: refactor image update flow and align i18n messages (#12154)
Refs #10786
This commit is contained in:
@@ -951,11 +951,13 @@ const message = {
|
||||
sameImageHelper: 'Containers using the same image can be batch upgraded after selection',
|
||||
targetImage: 'Target image',
|
||||
imageLoadErr: 'No image name detected for the container',
|
||||
imageUpdateTagEmpty: 'No updatable image tags detected',
|
||||
appHelper: 'The container comes from the app store, and upgrading may make the service unavailable.',
|
||||
resource: 'Resource',
|
||||
input: 'Manually input',
|
||||
forcePull: 'Always pull image ',
|
||||
forcePullHelper: 'This will ignore existing images on the server and pull the latest image from the registry.',
|
||||
imageUpdateHelper: 'Check the same tag in registry, and pull to update local image only if changed.',
|
||||
server: 'Host',
|
||||
serverExample: '80, 80-88, ip:80 or ip:80-88',
|
||||
containerExample: '80 or 80-88',
|
||||
|
||||
@@ -969,6 +969,7 @@ const message = {
|
||||
'Los contenedores que usan la misma imagen pueden actualizarse en lote después de seleccionarlos',
|
||||
targetImage: 'Imagen objetivo',
|
||||
imageLoadErr: 'No se detectó un nombre de imagen para el contenedor',
|
||||
imageUpdateTagEmpty: 'No se detectaron etiquetas de imagen actualizables',
|
||||
appHelper:
|
||||
'El contenedor proviene de la tienda de aplicaciones, y al actualizar podría hacer que el servicio no esté disponible.',
|
||||
resource: 'Recurso',
|
||||
@@ -976,6 +977,8 @@ const message = {
|
||||
forcePull: 'Siempre descargar imagen',
|
||||
forcePullHelper:
|
||||
'Esto ignorará las imágenes existentes en el servidor y descargará la más reciente desde el repositorio.',
|
||||
imageUpdateHelper:
|
||||
'Comprobar la misma etiqueta en el registro y, si hay cambios, descargar y actualizar la imagen local.',
|
||||
server: 'Servidor',
|
||||
serverExample: '80, 80-88, ip:80 o ip:80-88',
|
||||
containerExample: '80 o 80-88',
|
||||
|
||||
@@ -952,11 +952,13 @@ const message = {
|
||||
sameImageContainer: '同一イメージコンテナ',
|
||||
sameImageHelper: '同一イメージを使用するコンテナは選択後一括アップグレード可能',
|
||||
imageLoadErr: 'コンテナの画像名は検出されません',
|
||||
imageUpdateTagEmpty: '更新可能なイメージタグが検出されません',
|
||||
appHelper:
|
||||
'このコンテナはアプリストアから取得されたものであり、アップグレードによってサービスが利用不可になる可能性があります。',
|
||||
input: '手動入力',
|
||||
forcePull: '常に画像を引っ張ってください',
|
||||
forcePullHelper: 'これにより、サーバー上の既存の画像が無視され、レジストリから最新の画像が引き出されます。',
|
||||
imageUpdateHelper: 'レジストリの同名タグを確認し、更新があれば取得してローカルイメージを更新します。',
|
||||
server: 'ホスト',
|
||||
serverExample: '80、80-88、IP:80またはIP:80-88',
|
||||
containerExample: '80または80-88',
|
||||
|
||||
@@ -939,10 +939,12 @@ const message = {
|
||||
sameImageHelper: '동일한 이미지를 사용하는 컨테이너는 선택 후 일괄 업그레이드 가능',
|
||||
targetImage: '대상 이미지',
|
||||
imageLoadErr: '컨테이너에 대한 이미지 이름이 감지되지 않았습니다.',
|
||||
imageUpdateTagEmpty: '업데이트 가능한 이미지 태그를 찾지 못했습니다.',
|
||||
appHelper: '이 컨테이너는 앱 스토어에서 왔으며 업그레이드 시 서비스가 중단될 수 있습니다.',
|
||||
input: '수동 입력',
|
||||
forcePull: '이미지 강제 풀',
|
||||
forcePullHelper: '이 작업은 서버에 있는 기존 이미지를 무시하고 레지스트리에서 최신 이미지를 강제로 가져옵니다.',
|
||||
imageUpdateHelper: '레지스트리의 동일 태그를 확인하고, 변경이 있으면 가져와 로컬 이미지를 업데이트합니다.',
|
||||
server: '호스트',
|
||||
serverExample: '80, 80-88, ip:80 또는 ip:80-88',
|
||||
containerExample: '80 또는 80-88',
|
||||
|
||||
@@ -960,11 +960,14 @@ const message = {
|
||||
sameImageHelper: 'Kontena yang menggunakan imej sama boleh dinaik taraf secara berkumpulan setelah dipilih',
|
||||
targetImage: 'Imej sasaran',
|
||||
imageLoadErr: 'Tiada nama imej dikesan untuk kontena',
|
||||
imageUpdateTagEmpty: 'Tiada tag imej yang boleh dikemas kini dikesan',
|
||||
appHelper:
|
||||
'Kontena berasal dari gedung aplikasi, dan peningkatan boleh menyebabkan perkhidmatan tidak tersedia.',
|
||||
input: 'Input manual',
|
||||
forcePull: 'Tarik imej sentiasa ',
|
||||
forcePullHelper: 'Ini akan mengabaikan imej sedia ada di pelayan dan menarik imej terkini dari pendaftaran.',
|
||||
imageUpdateHelper:
|
||||
'Semak tag yang sama di registry, jika ada kemas kini barulah tarik dan kemas kini imej tempatan.',
|
||||
server: 'Hos',
|
||||
serverExample: '80, 80-88, ip:80 atau ip:80-88',
|
||||
containerExample: '80 atau 80-88',
|
||||
|
||||
@@ -958,11 +958,14 @@ const message = {
|
||||
sameImageHelper: 'Contêineres usando a mesma imagem podem ser atualizados em lote após seleção',
|
||||
targetImage: 'Imagem alvo',
|
||||
imageLoadErr: 'Nenhum nome de imagem detectado para o contêiner',
|
||||
imageUpdateTagEmpty: 'Nenhuma tag de imagem atualizável foi detectada',
|
||||
appHelper: 'O contêiner vem da loja de aplicativos, e o upgrade pode tornar o serviço indisponível.',
|
||||
input: 'Entrada manual',
|
||||
forcePull: 'Sempre puxar imagem',
|
||||
forcePullHelper:
|
||||
'Isso ignorará as imagens existentes no servidor e puxará a imagem mais recente do repositório.',
|
||||
imageUpdateHelper:
|
||||
'Verificar a mesma tag no registry e, se houver atualização, fazer pull e atualizar a imagem local.',
|
||||
server: 'Host',
|
||||
serverExample: '80, 80-88, ip:80 ou ip:80-88',
|
||||
containerExample: '80 ou 80-88',
|
||||
|
||||
@@ -953,11 +953,14 @@ const message = {
|
||||
sameImageHelper: 'Контейнеры, использующие один образ, можно массово обновить после выбора',
|
||||
targetImage: 'Целевой образ',
|
||||
imageLoadErr: 'Не обнаружено имя образа для контейнера',
|
||||
imageUpdateTagEmpty: 'Не обнаружены теги образов, доступные для обновления',
|
||||
appHelper: 'Контейнер происходит из магазина приложений, и обновление может сделать сервис недоступным.',
|
||||
input: 'Ручной ввод',
|
||||
forcePull: 'Всегда загружать образ',
|
||||
forcePullHelper:
|
||||
'Это будет игнорировать существующие образы на сервере и загружать последний образ из реестра.',
|
||||
imageUpdateHelper:
|
||||
'Проверить одноимённый тег в реестре и, если есть обновление, загрузить и обновить локальный образ.',
|
||||
server: 'Хост',
|
||||
serverExample: '80, 80-88, ip:80 или ip:80-88',
|
||||
containerExample: '80 или 80-88',
|
||||
|
||||
@@ -959,11 +959,13 @@ const message = {
|
||||
sameImageHelper: 'Aynı imajı kullanan konteynerlar seçilerek toplu şekilde güncellenebilir',
|
||||
targetImage: 'Hedef imaj',
|
||||
imageLoadErr: 'Konteyner için imaj adı algılanmadı',
|
||||
imageUpdateTagEmpty: 'Güncellenebilir imaj etiketi algılanmadı',
|
||||
appHelper: 'Konteyner uygulama mağazasından geliyor ve yükseltme hizmeti kullanılamaz hale getirebilir.',
|
||||
resource: 'Kaynak',
|
||||
input: 'Manuel giriş',
|
||||
forcePull: 'Her zaman imajı çek ',
|
||||
forcePullHelper: 'Bu, sunucudaki mevcut imajları yok sayacak ve kayıt defterinden en son imajı çekecektir.',
|
||||
imageUpdateHelper: 'Kayıt deposundaki aynı etiketi kontrol et, güncelleme varsa çekip yerel imajı güncelle.',
|
||||
server: 'Ana bilgisayar',
|
||||
serverExample: '80, 80-88, ip:80 veya ip:80-88',
|
||||
containerExample: '80 veya 80-88',
|
||||
|
||||
@@ -904,11 +904,13 @@ const message = {
|
||||
sameImageHelper: '同映像容器可勾選後批次升級',
|
||||
targetImage: '目標映像',
|
||||
imageLoadErr: '未偵測到容器的映像名稱',
|
||||
imageUpdateTagEmpty: '未偵測到可更新的映像標籤',
|
||||
appHelper: '該容器來源於應用商店,升級可能導致該服務不可用',
|
||||
resource: '資源',
|
||||
input: '手動輸入',
|
||||
forcePull: '強制拉取映像',
|
||||
forcePullHelper: '忽略伺服器已存在的映像,重新拉取一次',
|
||||
imageUpdateHelper: '將檢查映像倉庫中的同名標籤,若有更新則拉取並更新本地映像。',
|
||||
server: '伺服器',
|
||||
serverExample: '80, 80-88, ip:80 或 ip:80-88',
|
||||
containerExample: '80 或 80-88',
|
||||
|
||||
@@ -905,12 +905,14 @@ const message = {
|
||||
sameImageHelper: '同镜像容器可勾选后批量升级',
|
||||
targetImage: '目标镜像',
|
||||
imageLoadErr: '未检测到容器的镜像名称',
|
||||
imageUpdateTagEmpty: '未检测到可更新的镜像标签',
|
||||
appHelper: '该容器来源于应用商店,升级可能导致该服务不可用',
|
||||
|
||||
resource: '资源',
|
||||
input: '手动输入',
|
||||
forcePull: '强制拉取镜像',
|
||||
forcePullHelper: '忽略服务器已存在的镜像,重新拉取一次',
|
||||
imageUpdateHelper: '将检查镜像仓库中的同名标签,若有更新则拉取并更新本地镜像。',
|
||||
server: '服务器',
|
||||
serverExample: '80, 80-88, ip:80 或 ip:80-88',
|
||||
containerExample: '80 或 80-88',
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<el-button type="primary" plain @click="onOpenPull">
|
||||
{{ $t('container.imagePull') }}
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="onOpenload">
|
||||
<el-button type="primary" plain @click="onOpenLoad">
|
||||
{{ $t('container.importImage') }}
|
||||
</el-button>
|
||||
<el-button type="primary" plain @click="onOpenBuild">
|
||||
@@ -102,7 +102,7 @@
|
||||
/>
|
||||
<fu-table-operations
|
||||
width="250px"
|
||||
:ellipsis="10"
|
||||
:ellipsis="2"
|
||||
:buttons="buttons"
|
||||
:label="$t('commons.table.operate')"
|
||||
/>
|
||||
@@ -111,6 +111,36 @@
|
||||
</LayoutContent>
|
||||
|
||||
<CodemirrorDrawer ref="myDetail" />
|
||||
<DialogPro v-model="updateDialogVisible" :title="$t('commons.button.update')" @close="handleUpdateDialogClose">
|
||||
<el-form label-position="top">
|
||||
<el-form-item :label="$t('container.tag')">
|
||||
<el-checkbox
|
||||
class="w-full"
|
||||
:model-value="updateCheckAll"
|
||||
:indeterminate="updateIndeterminate"
|
||||
@change="onUpdateCheckAllChange"
|
||||
>
|
||||
{{ $t('commons.table.all') }}
|
||||
</el-checkbox>
|
||||
<el-checkbox-group v-model="updateSelectedTags">
|
||||
<el-checkbox v-for="tag in updateTagOptions" :key="tag" :label="tag">
|
||||
{{ tag }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<span class="input-help">{{ $t('container.imageUpdateHelper') }}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleUpdateDialogClose">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="submitUpdateSelection">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DialogPro>
|
||||
|
||||
<OpDialog ref="opRef" @search="search" />
|
||||
<Pull ref="dialogPullRef" @search="search" />
|
||||
@@ -140,12 +170,12 @@ import Prune from '@/views/container/image/prune/index.vue';
|
||||
import DockerStatus from '@/views/container/docker-status/index.vue';
|
||||
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import { searchImage, listImageRepo, imageRemove, inspect, containerPrune } from '@/api/modules/container';
|
||||
import { searchImage, listImageRepo, imageRemove, inspect, containerPrune, imagePull } from '@/api/modules/container';
|
||||
import i18n from '@/lang';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { ElMessageBox } from 'element-plus';
|
||||
import { updateCommonDescription } from '@/api/modules/setting';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
const taskLogRef = ref();
|
||||
@@ -184,6 +214,15 @@ const dialogSaveRef = ref();
|
||||
const dialogBuildRef = ref();
|
||||
const dialogDeleteRef = ref();
|
||||
const dialogPruneRef = ref();
|
||||
const updateDialogVisible = ref(false);
|
||||
const updateTagOptions = ref<Array<string>>([]);
|
||||
const updateSelectedTags = ref<Array<string>>([]);
|
||||
const updateCheckAll = computed(
|
||||
() => updateTagOptions.value.length > 0 && updateSelectedTags.value.length === updateTagOptions.value.length,
|
||||
);
|
||||
const updateIndeterminate = computed(
|
||||
() => updateSelectedTags.value.length > 0 && updateSelectedTags.value.length < updateTagOptions.value.length,
|
||||
);
|
||||
|
||||
const search = async (column?: any) => {
|
||||
if (!isActive.value || !isExist.value) {
|
||||
@@ -304,22 +343,82 @@ const openTaskLog = (taskID: string) => {
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
};
|
||||
|
||||
const onOpenload = () => {
|
||||
const onOpenLoad = () => {
|
||||
dialogLoadRef.value!.acceptParams();
|
||||
};
|
||||
|
||||
const normalizeImageTags = (tags: string[]) => {
|
||||
return (tags || []).filter((tag) => tag && !tag.includes('<none>'));
|
||||
};
|
||||
|
||||
const pullImageTags = async (tags: string[]) => {
|
||||
const taskID = newUUID();
|
||||
await imagePull({
|
||||
taskID: taskID,
|
||||
repoID: 0,
|
||||
imageName: tags,
|
||||
});
|
||||
openTaskLog(taskID);
|
||||
};
|
||||
|
||||
const runUpdate = async (tags: string[]) => {
|
||||
const validTags = normalizeImageTags(tags);
|
||||
if (validTags.length === 0) {
|
||||
MsgError(i18n.global.t('container.imageUpdateTagEmpty'));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
i18n.global.t('container.imageUpdateHelper'),
|
||||
i18n.global.t('commons.button.update'),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'info',
|
||||
},
|
||||
);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
await pullImageTags(validTags);
|
||||
};
|
||||
|
||||
const onUpdate = async (row: Container.ImageInfo) => {
|
||||
const tags = normalizeImageTags(row.tags || []);
|
||||
if (tags.length === 0) {
|
||||
MsgError(i18n.global.t('container.imageUpdateTagEmpty'));
|
||||
return;
|
||||
}
|
||||
if (tags.length === 1) {
|
||||
await runUpdate(tags);
|
||||
return;
|
||||
}
|
||||
updateTagOptions.value = tags;
|
||||
updateSelectedTags.value = [...tags];
|
||||
updateDialogVisible.value = true;
|
||||
};
|
||||
|
||||
const onUpdateCheckAllChange = (checked: boolean) => {
|
||||
updateSelectedTags.value = checked ? [...updateTagOptions.value] : [];
|
||||
};
|
||||
|
||||
const handleUpdateDialogClose = () => {
|
||||
updateDialogVisible.value = false;
|
||||
updateTagOptions.value = [];
|
||||
updateSelectedTags.value = [];
|
||||
};
|
||||
|
||||
const submitUpdateSelection = async () => {
|
||||
if (updateSelectedTags.value.length === 0) {
|
||||
MsgError(i18n.global.t('commons.msg.confirmNoNull', [i18n.global.t('container.tag')]));
|
||||
return;
|
||||
}
|
||||
const selected = [...updateSelectedTags.value];
|
||||
handleUpdateDialogClose();
|
||||
await runUpdate(selected);
|
||||
};
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('container.tag'),
|
||||
click: (row: Container.ImageInfo) => {
|
||||
let params = {
|
||||
repos: repos.value,
|
||||
imageID: row.id,
|
||||
tags: row.tags,
|
||||
};
|
||||
dialogTagRef.value!.acceptParams(params);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('container.push'),
|
||||
click: (row: Container.ImageInfo) => {
|
||||
@@ -340,6 +439,23 @@ const buttons = [
|
||||
dialogSaveRef.value!.acceptParams(params);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.update'),
|
||||
click: (row: Container.ImageInfo) => {
|
||||
onUpdate(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('container.tag'),
|
||||
click: (row: Container.ImageInfo) => {
|
||||
let params = {
|
||||
repos: repos.value,
|
||||
imageID: row.id,
|
||||
tags: row.tags,
|
||||
};
|
||||
dialogTagRef.value!.acceptParams(params);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
click: async (row: Container.ImageInfo) => {
|
||||
|
||||
Reference in New Issue
Block a user