feat: 容器创建替换可输入选择框 (#2378)

This commit is contained in:
ssongliu 2023-09-22 15:58:23 +08:00 committed by GitHub
parent 1905e55628
commit 38dadf6056
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 76 deletions

View File

@ -20,6 +20,7 @@ export namespace Container {
containerID: string; containerID: string;
name: string; name: string;
image: string; image: string;
imageInput: boolean;
forcePull: boolean; forcePull: boolean;
network: string; network: string;
cmdStr: string; cmdStr: string;
@ -51,6 +52,7 @@ export namespace Container {
sourceDir: string; sourceDir: string;
containerDir: string; containerDir: string;
mode: string; mode: string;
isVolume: boolean;
} }
export interface ContainerInfo { export interface ContainerInfo {
containerID: string; containerID: string;

View File

@ -476,7 +476,7 @@ const message = {
container: { container: {
create: 'Create container', create: 'Create container',
edit: 'Edit 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?', 'Container editing requires rebuilding the container. Any data that has not been persisted will be lost. Do you want to continue?',
containerList: 'Container list', containerList: 'Container list',
operatorHelper: '{0} will be performed on the following container, Do you want to continue?', operatorHelper: '{0} will be performed on the following container, Do you want to continue?',
@ -551,6 +551,7 @@ const message = {
appHelper: appHelper:
'This container is sourced from the application store. Upgrading it may cause the service to be unavailable.', 'This container is sourced from the application store. Upgrading it may cause the service to be unavailable.',
input: 'Input',
forcePull: 'forced image pull ', forcePull: 'forced image pull ',
forcePullHelper: 'Ignore existing images on the server and pull again.', forcePullHelper: 'Ignore existing images on the server and pull again.',
server: 'Host', server: 'Host',
@ -565,6 +566,8 @@ const message = {
memoryLimit: 'Memory', memoryLimit: 'Memory',
limitHelper: 'If you limit it to 0, then the limitation is turned off, and the maximum available is {0}.', limitHelper: 'If you limit it to 0, then the limitation is turned off, and the maximum available is {0}.',
mount: 'Mount', mount: 'Mount',
volumeOption: 'Volume',
hostOption: 'Host',
serverPath: 'Server path', serverPath: 'Server path',
containerDir: 'Container path', containerDir: 'Container path',
volumeHelper: 'Ensure that the content of the storage volume is correct', volumeHelper: 'Ensure that the content of the storage volume is correct',

View File

@ -465,7 +465,7 @@ const message = {
container: { container: {
create: '創建容器', create: '創建容器',
edit: '編輯容器', edit: '編輯容器',
updateContaienrHelper: '容器編輯需要重建容器任何未持久化的數據將會丟失是否繼續', updateContainerHelper: '容器編輯需要重建容器任何未持久化的數據將會丟失是否繼續',
containerList: '容器列表', containerList: '容器列表',
operatorHelper: '將對以下容器進行 {0} 操作是否繼續', operatorHelper: '將對以下容器進行 {0} 操作是否繼續',
operatorAppHelper: operatorAppHelper:
@ -534,6 +534,7 @@ const message = {
targetImageHelper: '請輸入目標鏡像版本', targetImageHelper: '請輸入目標鏡像版本',
appHelper: '該容器來源於應用商店升級可能導致該服務不可用', appHelper: '該容器來源於應用商店升級可能導致該服務不可用',
input: '手動輸入',
forcePull: '強製拉取鏡像', forcePull: '強製拉取鏡像',
forcePullHelper: '忽略服務器已存在的鏡像重新拉取一次', forcePullHelper: '忽略服務器已存在的鏡像重新拉取一次',
server: '服務器', server: '服務器',
@ -547,7 +548,9 @@ const message = {
cpuQuota: 'CPU 限製', cpuQuota: 'CPU 限製',
memoryLimit: '內存限製', memoryLimit: '內存限製',
limitHelper: '限製為 0 則關閉限製最大可用為 {0}', limitHelper: '限製為 0 則關閉限製最大可用為 {0}',
mount: '掛載卷', mount: '掛載',
volumeOption: '掛載卷',
hostOption: '本機目錄',
serverPath: '服務器目錄', serverPath: '服務器目錄',
containerDir: '容器目錄', containerDir: '容器目錄',
volumeHelper: '請確認存儲卷內容輸入正確', volumeHelper: '請確認存儲卷內容輸入正確',

View File

@ -465,7 +465,7 @@ const message = {
container: { container: {
create: '创建容器', create: '创建容器',
edit: '编辑容器', edit: '编辑容器',
updateContaienrHelper: '容器编辑需要重建容器任何未持久化的数据将会丢失是否继续', updateContainerHelper: '容器编辑需要重建容器任何未持久化的数据将会丢失是否继续',
containerList: '容器列表', containerList: '容器列表',
operatorHelper: '将对以下容器进行 {0} 操作是否继续', operatorHelper: '将对以下容器进行 {0} 操作是否继续',
operatorAppHelper: operatorAppHelper:
@ -534,6 +534,7 @@ const message = {
targetImageHelper: '请输入目标镜像版本', targetImageHelper: '请输入目标镜像版本',
appHelper: '该容器来源于应用商店升级可能导致该服务不可用', appHelper: '该容器来源于应用商店升级可能导致该服务不可用',
input: '手动输入',
forcePull: '强制拉取镜像', forcePull: '强制拉取镜像',
forcePullHelper: '忽略服务器已存在的镜像重新拉取一次', forcePullHelper: '忽略服务器已存在的镜像重新拉取一次',
server: '服务器', server: '服务器',
@ -547,7 +548,9 @@ const message = {
cpuQuota: 'CPU 限制', cpuQuota: 'CPU 限制',
memoryLimit: '内存限制', memoryLimit: '内存限制',
limitHelper: '限制为 0 则关闭限制最大可用为 {0}', limitHelper: '限制为 0 则关闭限制最大可用为 {0}',
mount: '挂载卷', mount: '挂载',
volumeOption: '挂载卷',
hostOption: '本机目录',
serverPath: '服务器目录', serverPath: '服务器目录',
containerDir: '容器目录', containerDir: '容器目录',
volumeHelper: '请确认存储卷内容输入正确', volumeHelper: '请确认存储卷内容输入正确',

View File

@ -1,5 +1,5 @@
<template> <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> <template #header>
<DrawerHeader <DrawerHeader
:header="title" :header="title"
@ -22,10 +22,9 @@
<el-input clearable v-model.trim="dialogData.rowData!.name" /> <el-input clearable v-model.trim="dialogData.rowData!.name" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('container.image')" prop="image"> <el-form-item :label="$t('container.image')" prop="image">
<el-checkbox v-model="dialogData.rowData!.imageInput" :label="$t('container.input')" />
<el-select <el-select
class="widthClass" v-if="!dialogData.rowData!.imageInput"
:placeholder="$t('commons.msg.inputOrSelect')"
allow-create
filterable filterable
v-model="dialogData.rowData!.image" v-model="dialogData.rowData!.image"
> >
@ -36,6 +35,7 @@
:label="item.option" :label="item.option"
/> />
</el-select> </el-select>
<el-input v-else v-model="dialogData.rowData!.image" />
</el-form-item> </el-form-item>
<el-form-item prop="forcePull"> <el-form-item prop="forcePull">
<el-checkbox v-model="dialogData.rowData!.forcePull"> <el-checkbox v-model="dialogData.rowData!.forcePull">
@ -110,64 +110,57 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="$t('container.mount')"> <el-form-item :label="$t('container.mount')">
<el-card style="width: 100%"> <div v-for="(row, index) in dialogData.rowData!.volumes" :key="index" style="width: 100%">
<table style="width: 100%" class="tab-table"> <el-card class="mt-1">
<tr v-if="dialogData.rowData!.volumes!.length !== 0"> <el-radio-group v-model="row.isVolume">
<th scope="col" width="39%" align="left"> <el-radio-button :label="true">{{ $t('container.volumeOption') }}</el-radio-button>
<label>{{ $t('container.serverPath') }}</label> <el-radio-button :label="false">{{ $t('container.hostOption') }}</el-radio-button>
</th> </el-radio-group>
<th scope="col" width="18%" align="left"> <el-button
<label>{{ $t('container.mode') }}</label> class="float-right mt-3"
</th> link
<th scope="col" width="39%" align="left"> type="primary"
<label>{{ $t('container.containerDir') }}</label> @click="handleVolumesDelete(index)"
</th> >
<th align="left"></th> {{ $t('commons.button.delete') }}
</tr> </el-button>
<tr v-for="(row, index) in dialogData.rowData!.volumes" :key="index"> <el-row class="mt-4" :gutter="5">
<td width="39%"> <el-col :span="10">
<el-select <el-form-item v-if="row.isVolume" :label="$t('container.volumeOption')">
class="widthClass" <el-select filterable v-model="row.sourceDir">
allow-create <div v-for="(item, indexV) of volumes" :key="indexV">
clearable <el-tooltip :hide-after="20" :content="item.option" placement="top">
:placeholder="$t('commons.msg.inputOrSelect')" <el-option
filterable :value="item.option"
v-model="row.sourceDir" :label="item.option.substring(0, 30)"
> />
<div v-for="(item, indexV) of volumes" :key="indexV"> </el-tooltip>
<el-tooltip :hide-after="20" :content="item.option" placement="top"> </div>
<el-option </el-select>
:value="item.option" </el-form-item>
:label="item.option.substring(0, 30)" <el-form-item v-else :label="$t('container.hostOption')">
/> <el-input v-model="row.sourceDir" />
</el-tooltip> </el-form-item>
</div> </el-col>
</el-select> <el-col :span="5">
</td> <el-form-item :label="$t('container.mode')">
<td width="18%"> <el-select class="widthClass" filterable v-model="row.mode">
<el-select class="widthClass" filterable v-model="row.mode"> <el-option value="rw" :label="$t('container.modeRW')" />
<el-option value="rw" :label="$t('container.modeRW')" /> <el-option value="ro" :label="$t('container.modeR')" />
<el-option value="ro" :label="$t('container.modeR')" /> </el-select>
</el-select> </el-form-item>
</td> </el-col>
<td width="39%"> <el-col :span="9">
<el-input v-model="row.containerDir" /> <el-form-item :label="$t('container.containerDir')">
</td> <el-input v-model="row.containerDir" />
<td> </el-form-item>
<el-button link style="font-size: 10px" @click="handleVolumesDelete(index)"> </el-col>
{{ $t('commons.button.delete') }} </el-row>
</el-button> </el-card>
</td> </div>
</tr> <el-button @click="handleVolumesAdd()">
<tr> {{ $t('commons.button.add') }}
<td align="left"> </el-button>
<el-button @click="handleVolumesAdd()">
{{ $t('commons.button.add') }}
</el-button>
</td>
</tr>
</table>
</el-card>
</el-form-item> </el-form-item>
<el-form-item label="Command" prop="cmdStr"> <el-form-item label="Command" prop="cmdStr">
<el-input v-model="dialogData.rowData!.cmdStr" :placeholder="$t('container.cmdHelper')" /> <el-input v-model="dialogData.rowData!.cmdStr" :placeholder="$t('container.cmdHelper')" />
@ -236,7 +229,7 @@
</el-form> </el-form>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button :disabled="loading" @click="drawerVisiable = false"> <el-button :disabled="loading" @click="drawerVisible = false">
{{ $t('commons.button.cancel') }} {{ $t('commons.button.cancel') }}
</el-button> </el-button>
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)"> <el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
@ -251,7 +244,7 @@
import { reactive, ref } from 'vue'; import { reactive, ref } from 'vue';
import { Rules, checkFloatNumberRange, checkNumberRange } from '@/global/form-rules'; import { Rules, checkFloatNumberRange, checkNumberRange } from '@/global/form-rules';
import i18n from '@/lang'; import i18n from '@/lang';
import { ElForm } from 'element-plus'; import { ElForm, ElMessageBox } from 'element-plus';
import DrawerHeader from '@/components/drawer-header/index.vue'; import DrawerHeader from '@/components/drawer-header/index.vue';
import { import {
listImage, listImage,
@ -273,7 +266,7 @@ interface DialogProps {
} }
const title = ref<string>(''); const title = ref<string>('');
const drawerVisiable = ref(false); const drawerVisible = ref(false);
const dialogData = ref<DialogProps>({ const dialogData = ref<DialogProps>({
title: '', title: '',
@ -310,7 +303,7 @@ const acceptParams = (params: DialogProps): void => {
loadImageOptions(); loadImageOptions();
loadVolumeOptions(); loadVolumeOptions();
loadNetworkOptions(); loadNetworkOptions();
drawerVisiable.value = true; drawerVisible.value = true;
}; };
const emit = defineEmits<{ (e: 'search'): void }>(); const emit = defineEmits<{ (e: 'search'): void }>();
@ -324,12 +317,12 @@ const limits = ref<Container.ResourceLimit>({
const handleClose = () => { const handleClose = () => {
emit('search'); emit('search');
drawerVisiable.value = false; drawerVisible.value = false;
}; };
const rules = reactive({ const rules = reactive({
name: [Rules.requiredInput, Rules.volumeName], name: [Rules.requiredInput, Rules.volumeName],
image: [Rules.requiredSelect], image: [Rules.requiredInput],
cpuShares: [Rules.integerNumberWith0, checkNumberRange(0, 262144)], cpuShares: [Rules.integerNumberWith0, checkNumberRange(0, 262144)],
nanoCPUs: [Rules.floatNumber], nanoCPUs: [Rules.floatNumber],
memory: [Rules.floatNumber], memory: [Rules.floatNumber],
@ -357,6 +350,7 @@ const handleVolumesAdd = () => {
sourceDir: '', sourceDir: '',
containerDir: '', containerDir: '',
mode: 'rw', mode: 'rw',
isVolume: true,
}; };
dialogData.value.rowData!.volumes.push(item); dialogData.value.rowData!.volumes.push(item);
}; };
@ -377,6 +371,18 @@ const loadImageOptions = async () => {
const loadVolumeOptions = async () => { const loadVolumeOptions = async () => {
const res = await listVolume(); const res = await listVolume();
volumes.value = res.data; 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 loadNetworkOptions = async () => {
const res = await listNetwork(); const res = await listNetwork();
@ -436,14 +442,14 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search'); emit('search');
drawerVisiable.value = false; drawerVisible.value = false;
}) })
.catch(() => { .catch(() => {
loading.value = false; loading.value = false;
}); });
} else { } else {
ElMessageBox.confirm( ElMessageBox.confirm(
i18n.global.t('container.updateContaienrHelper'), i18n.global.t('container.updateContainerHelper'),
i18n.global.t('commons.button.edit'), i18n.global.t('commons.button.edit'),
{ {
confirmButtonText: i18n.global.t('commons.button.confirm'), confirmButtonText: i18n.global.t('commons.button.confirm'),
@ -456,7 +462,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
loading.value = false; loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
emit('search'); emit('search');
drawerVisiable.value = false; drawerVisible.value = false;
}) })
.catch(() => { .catch(() => {
loading.value = false; loading.value = false;