mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-30 23:49:10 +08:00
feat: 容器创建替换可输入选择框 (#2378)
This commit is contained in:
parent
1905e55628
commit
38dadf6056
@ -20,6 +20,7 @@ export namespace Container {
|
||||
containerID: string;
|
||||
name: string;
|
||||
image: string;
|
||||
imageInput: boolean;
|
||||
forcePull: boolean;
|
||||
network: string;
|
||||
cmdStr: string;
|
||||
@ -51,6 +52,7 @@ export namespace Container {
|
||||
sourceDir: string;
|
||||
containerDir: string;
|
||||
mode: string;
|
||||
isVolume: boolean;
|
||||
}
|
||||
export interface ContainerInfo {
|
||||
containerID: string;
|
||||
|
@ -476,7 +476,7 @@ const message = {
|
||||
container: {
|
||||
create: 'Create container',
|
||||
edit: 'Edit container',
|
||||
updateContaienrHelper:
|
||||
updateContainerHelper:
|
||||
'Container editing requires rebuilding the container. Any data that has not been persisted will be lost. Do you want to continue?',
|
||||
containerList: 'Container list',
|
||||
operatorHelper: '{0} will be performed on the following container, Do you want to continue?',
|
||||
@ -551,6 +551,7 @@ const message = {
|
||||
appHelper:
|
||||
'This container is sourced from the application store. Upgrading it may cause the service to be unavailable.',
|
||||
|
||||
input: 'Input',
|
||||
forcePull: 'forced image pull ',
|
||||
forcePullHelper: 'Ignore existing images on the server and pull again.',
|
||||
server: 'Host',
|
||||
@ -565,6 +566,8 @@ const message = {
|
||||
memoryLimit: 'Memory',
|
||||
limitHelper: 'If you limit it to 0, then the limitation is turned off, and the maximum available is {0}.',
|
||||
mount: 'Mount',
|
||||
volumeOption: 'Volume',
|
||||
hostOption: 'Host',
|
||||
serverPath: 'Server path',
|
||||
containerDir: 'Container path',
|
||||
volumeHelper: 'Ensure that the content of the storage volume is correct',
|
||||
|
@ -465,7 +465,7 @@ const message = {
|
||||
container: {
|
||||
create: '創建容器',
|
||||
edit: '編輯容器',
|
||||
updateContaienrHelper: '容器編輯需要重建容器,任何未持久化的數據將會丟失,是否繼續?',
|
||||
updateContainerHelper: '容器編輯需要重建容器,任何未持久化的數據將會丟失,是否繼續?',
|
||||
containerList: '容器列表',
|
||||
operatorHelper: '將對以下容器進行 {0} 操作,是否繼續?',
|
||||
operatorAppHelper:
|
||||
@ -534,6 +534,7 @@ const message = {
|
||||
targetImageHelper: '請輸入目標鏡像版本',
|
||||
appHelper: '該容器來源於應用商店,升級可能導致該服務不可用',
|
||||
|
||||
input: '手動輸入',
|
||||
forcePull: '強製拉取鏡像',
|
||||
forcePullHelper: '忽略服務器已存在的鏡像,重新拉取一次',
|
||||
server: '服務器',
|
||||
@ -547,7 +548,9 @@ const message = {
|
||||
cpuQuota: 'CPU 限製',
|
||||
memoryLimit: '內存限製',
|
||||
limitHelper: '限製為 0 則關閉限製,最大可用為 {0}',
|
||||
mount: '掛載卷',
|
||||
mount: '掛載',
|
||||
volumeOption: '掛載卷',
|
||||
hostOption: '本機目錄',
|
||||
serverPath: '服務器目錄',
|
||||
containerDir: '容器目錄',
|
||||
volumeHelper: '請確認存儲卷內容輸入正確',
|
||||
|
@ -465,7 +465,7 @@ const message = {
|
||||
container: {
|
||||
create: '创建容器',
|
||||
edit: '编辑容器',
|
||||
updateContaienrHelper: '容器编辑需要重建容器,任何未持久化的数据将会丢失,是否继续?',
|
||||
updateContainerHelper: '容器编辑需要重建容器,任何未持久化的数据将会丢失,是否继续?',
|
||||
containerList: '容器列表',
|
||||
operatorHelper: '将对以下容器进行 {0} 操作,是否继续?',
|
||||
operatorAppHelper:
|
||||
@ -534,6 +534,7 @@ const message = {
|
||||
targetImageHelper: '请输入目标镜像版本',
|
||||
appHelper: '该容器来源于应用商店,升级可能导致该服务不可用',
|
||||
|
||||
input: '手动输入',
|
||||
forcePull: '强制拉取镜像',
|
||||
forcePullHelper: '忽略服务器已存在的镜像,重新拉取一次',
|
||||
server: '服务器',
|
||||
@ -547,7 +548,9 @@ const message = {
|
||||
cpuQuota: 'CPU 限制',
|
||||
memoryLimit: '内存限制',
|
||||
limitHelper: '限制为 0 则关闭限制,最大可用为 {0}',
|
||||
mount: '挂载卷',
|
||||
mount: '挂载',
|
||||
volumeOption: '挂载卷',
|
||||
hostOption: '本机目录',
|
||||
serverPath: '服务器目录',
|
||||
containerDir: '容器目录',
|
||||
volumeHelper: '请确认存储卷内容输入正确',
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
|
||||
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
|
||||
<template #header>
|
||||
<DrawerHeader
|
||||
:header="title"
|
||||
@ -22,10 +22,9 @@
|
||||
<el-input clearable v-model.trim="dialogData.rowData!.name" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.image')" prop="image">
|
||||
<el-checkbox v-model="dialogData.rowData!.imageInput" :label="$t('container.input')" />
|
||||
<el-select
|
||||
class="widthClass"
|
||||
:placeholder="$t('commons.msg.inputOrSelect')"
|
||||
allow-create
|
||||
v-if="!dialogData.rowData!.imageInput"
|
||||
filterable
|
||||
v-model="dialogData.rowData!.image"
|
||||
>
|
||||
@ -36,6 +35,7 @@
|
||||
:label="item.option"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input v-else v-model="dialogData.rowData!.image" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="forcePull">
|
||||
<el-checkbox v-model="dialogData.rowData!.forcePull">
|
||||
@ -110,64 +110,57 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.mount')">
|
||||
<el-card style="width: 100%">
|
||||
<table style="width: 100%" class="tab-table">
|
||||
<tr v-if="dialogData.rowData!.volumes!.length !== 0">
|
||||
<th scope="col" width="39%" align="left">
|
||||
<label>{{ $t('container.serverPath') }}</label>
|
||||
</th>
|
||||
<th scope="col" width="18%" align="left">
|
||||
<label>{{ $t('container.mode') }}</label>
|
||||
</th>
|
||||
<th scope="col" width="39%" align="left">
|
||||
<label>{{ $t('container.containerDir') }}</label>
|
||||
</th>
|
||||
<th align="left"></th>
|
||||
</tr>
|
||||
<tr v-for="(row, index) in dialogData.rowData!.volumes" :key="index">
|
||||
<td width="39%">
|
||||
<el-select
|
||||
class="widthClass"
|
||||
allow-create
|
||||
clearable
|
||||
:placeholder="$t('commons.msg.inputOrSelect')"
|
||||
filterable
|
||||
v-model="row.sourceDir"
|
||||
>
|
||||
<div v-for="(item, indexV) of volumes" :key="indexV">
|
||||
<el-tooltip :hide-after="20" :content="item.option" placement="top">
|
||||
<el-option
|
||||
:value="item.option"
|
||||
:label="item.option.substring(0, 30)"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-select>
|
||||
</td>
|
||||
<td width="18%">
|
||||
<el-select class="widthClass" filterable v-model="row.mode">
|
||||
<el-option value="rw" :label="$t('container.modeRW')" />
|
||||
<el-option value="ro" :label="$t('container.modeR')" />
|
||||
</el-select>
|
||||
</td>
|
||||
<td width="39%">
|
||||
<el-input v-model="row.containerDir" />
|
||||
</td>
|
||||
<td>
|
||||
<el-button link style="font-size: 10px" @click="handleVolumesDelete(index)">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<el-button @click="handleVolumesAdd()">
|
||||
{{ $t('commons.button.add') }}
|
||||
</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</el-card>
|
||||
<div v-for="(row, index) in dialogData.rowData!.volumes" :key="index" style="width: 100%">
|
||||
<el-card class="mt-1">
|
||||
<el-radio-group v-model="row.isVolume">
|
||||
<el-radio-button :label="true">{{ $t('container.volumeOption') }}</el-radio-button>
|
||||
<el-radio-button :label="false">{{ $t('container.hostOption') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-button
|
||||
class="float-right mt-3"
|
||||
link
|
||||
type="primary"
|
||||
@click="handleVolumesDelete(index)"
|
||||
>
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
<el-row class="mt-4" :gutter="5">
|
||||
<el-col :span="10">
|
||||
<el-form-item v-if="row.isVolume" :label="$t('container.volumeOption')">
|
||||
<el-select filterable v-model="row.sourceDir">
|
||||
<div v-for="(item, indexV) of volumes" :key="indexV">
|
||||
<el-tooltip :hide-after="20" :content="item.option" placement="top">
|
||||
<el-option
|
||||
:value="item.option"
|
||||
:label="item.option.substring(0, 30)"
|
||||
/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-else :label="$t('container.hostOption')">
|
||||
<el-input v-model="row.sourceDir" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="5">
|
||||
<el-form-item :label="$t('container.mode')">
|
||||
<el-select class="widthClass" filterable v-model="row.mode">
|
||||
<el-option value="rw" :label="$t('container.modeRW')" />
|
||||
<el-option value="ro" :label="$t('container.modeR')" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="9">
|
||||
<el-form-item :label="$t('container.containerDir')">
|
||||
<el-input v-model="row.containerDir" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
<el-button @click="handleVolumesAdd()">
|
||||
{{ $t('commons.button.add') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="Command" prop="cmdStr">
|
||||
<el-input v-model="dialogData.rowData!.cmdStr" :placeholder="$t('container.cmdHelper')" />
|
||||
@ -236,7 +229,7 @@
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button :disabled="loading" @click="drawerVisiable = false">
|
||||
<el-button :disabled="loading" @click="drawerVisible = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
||||
@ -251,7 +244,7 @@
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Rules, checkFloatNumberRange, checkNumberRange } from '@/global/form-rules';
|
||||
import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { ElForm, ElMessageBox } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import {
|
||||
listImage,
|
||||
@ -273,7 +266,7 @@ interface DialogProps {
|
||||
}
|
||||
|
||||
const title = ref<string>('');
|
||||
const drawerVisiable = ref(false);
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
const dialogData = ref<DialogProps>({
|
||||
title: '',
|
||||
@ -310,7 +303,7 @@ const acceptParams = (params: DialogProps): void => {
|
||||
loadImageOptions();
|
||||
loadVolumeOptions();
|
||||
loadNetworkOptions();
|
||||
drawerVisiable.value = true;
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
@ -324,12 +317,12 @@ const limits = ref<Container.ResourceLimit>({
|
||||
|
||||
const handleClose = () => {
|
||||
emit('search');
|
||||
drawerVisiable.value = false;
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
const rules = reactive({
|
||||
name: [Rules.requiredInput, Rules.volumeName],
|
||||
image: [Rules.requiredSelect],
|
||||
image: [Rules.requiredInput],
|
||||
cpuShares: [Rules.integerNumberWith0, checkNumberRange(0, 262144)],
|
||||
nanoCPUs: [Rules.floatNumber],
|
||||
memory: [Rules.floatNumber],
|
||||
@ -357,6 +350,7 @@ const handleVolumesAdd = () => {
|
||||
sourceDir: '',
|
||||
containerDir: '',
|
||||
mode: 'rw',
|
||||
isVolume: true,
|
||||
};
|
||||
dialogData.value.rowData!.volumes.push(item);
|
||||
};
|
||||
@ -377,6 +371,18 @@ const loadImageOptions = async () => {
|
||||
const loadVolumeOptions = async () => {
|
||||
const res = await listVolume();
|
||||
volumes.value = res.data;
|
||||
for (const item of dialogData.value.rowData.volumes) {
|
||||
let isVolume = false;
|
||||
for (const v of volumes.value) {
|
||||
if (item.sourceDir == v.option) {
|
||||
item.isVolume = true;
|
||||
break;
|
||||
}
|
||||
if (!isVolume) {
|
||||
item.isVolume = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const loadNetworkOptions = async () => {
|
||||
const res = await listNetwork();
|
||||
@ -436,14 +442,14 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
emit('search');
|
||||
drawerVisiable.value = false;
|
||||
drawerVisible.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
} else {
|
||||
ElMessageBox.confirm(
|
||||
i18n.global.t('container.updateContaienrHelper'),
|
||||
i18n.global.t('container.updateContainerHelper'),
|
||||
i18n.global.t('commons.button.edit'),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
@ -456,7 +462,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
emit('search');
|
||||
drawerVisiable.value = false;
|
||||
drawerVisible.value = false;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
|
Loading…
Reference in New Issue
Block a user