mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-28 09:46:28 +08:00
feat: 增加容器状态统计及状态快速切换 (#6594)
Some checks failed
sync2gitee / repo-sync (push) Failing after -9m34s
Some checks failed
sync2gitee / repo-sync (push) Failing after -9m34s
This commit is contained in:
parent
1e1fce4c77
commit
c101dfb694
10 changed files with 634 additions and 448 deletions
|
|
@ -54,6 +54,24 @@ func (b *BaseApi) ListContainer(c *gin.Context) {
|
||||||
helper.SuccessWithData(c, list)
|
helper.SuccessWithData(c, list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// @Tags Container
|
||||||
|
// @Summary Load containers status
|
||||||
|
// @Description 获取容器状态
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /containers/status [get]
|
||||||
|
func (b *BaseApi) LoadContainerStatus(c *gin.Context) {
|
||||||
|
data, err := containerService.LoadStatus()
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags Container Compose
|
// @Tags Container Compose
|
||||||
// @Summary Page composes
|
// @Summary Page composes
|
||||||
// @Description 获取编排列表分页
|
// @Description 获取编排列表分页
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ type PageContainer struct {
|
||||||
PageInfo
|
PageInfo
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
|
State string `json:"state" validate:"required,oneof=all created running paused restarting removing exited dead"`
|
||||||
OrderBy string `json:"orderBy" validate:"required,oneof=name state created_at"`
|
OrderBy string `json:"orderBy" validate:"required,oneof=name created_at"`
|
||||||
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
Order string `json:"order" validate:"required,oneof=null ascending descending"`
|
||||||
Filters string `json:"filters"`
|
Filters string `json:"filters"`
|
||||||
ExcludeAppStore bool `json:"excludeAppStore"`
|
ExcludeAppStore bool `json:"excludeAppStore"`
|
||||||
|
|
@ -39,6 +39,16 @@ type ContainerInfo struct {
|
||||||
Websites []string `json:"websites"`
|
Websites []string `json:"websites"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContainerStatus struct {
|
||||||
|
All uint `json:"all"`
|
||||||
|
Created uint `json:"created"`
|
||||||
|
Running uint `json:"running"`
|
||||||
|
Paused uint `json:"paused"`
|
||||||
|
Restarting uint `json:"restarting"`
|
||||||
|
Removing uint `json:"removing"`
|
||||||
|
Exited uint `json:"exited"`
|
||||||
|
Dead uint `json:"dead"`
|
||||||
|
}
|
||||||
type ResourceLimit struct {
|
type ResourceLimit struct {
|
||||||
CPU int `json:"cpu"`
|
CPU int `json:"cpu"`
|
||||||
Memory uint64 `json:"memory"`
|
Memory uint64 `json:"memory"`
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ type ContainerService struct{}
|
||||||
type IContainerService interface {
|
type IContainerService interface {
|
||||||
Page(req dto.PageContainer) (int64, interface{}, error)
|
Page(req dto.PageContainer) (int64, interface{}, error)
|
||||||
List() ([]string, error)
|
List() ([]string, error)
|
||||||
|
LoadStatus() (dto.ContainerStatus, error)
|
||||||
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
|
PageNetwork(req dto.SearchWithPage) (int64, interface{}, error)
|
||||||
ListNetwork() ([]dto.Options, error)
|
ListNetwork() ([]dto.Options, error)
|
||||||
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
|
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
|
||||||
|
|
@ -149,13 +150,6 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
|
||||||
}
|
}
|
||||||
return list[i].Names[0][1:] > list[j].Names[0][1:]
|
return list[i].Names[0][1:] > list[j].Names[0][1:]
|
||||||
})
|
})
|
||||||
case "state":
|
|
||||||
sort.Slice(list, func(i, j int) bool {
|
|
||||||
if req.Order == constant.OrderAsc {
|
|
||||||
return list[i].State < list[j].State
|
|
||||||
}
|
|
||||||
return list[i].State > list[j].State
|
|
||||||
})
|
|
||||||
default:
|
default:
|
||||||
sort.Slice(list, func(i, j int) bool {
|
sort.Slice(list, func(i, j int) bool {
|
||||||
if req.Order == constant.OrderAsc {
|
if req.Order == constant.OrderAsc {
|
||||||
|
|
@ -245,6 +239,38 @@ func (u *ContainerService) List() ([]string, error) {
|
||||||
return datas, nil
|
return datas, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ContainerService) LoadStatus() (dto.ContainerStatus, error) {
|
||||||
|
var data dto.ContainerStatus
|
||||||
|
client, err := docker.NewDockerClient()
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
containers, err := client.ContainerList(context.Background(), container.ListOptions{All: true})
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
|
}
|
||||||
|
data.All = uint(len(containers))
|
||||||
|
for _, item := range containers {
|
||||||
|
switch item.State {
|
||||||
|
case "created":
|
||||||
|
data.Created++
|
||||||
|
case "running":
|
||||||
|
data.Running++
|
||||||
|
case "paused":
|
||||||
|
data.Paused++
|
||||||
|
case "restarting":
|
||||||
|
data.Restarting++
|
||||||
|
case "dead":
|
||||||
|
data.Dead++
|
||||||
|
case "exited":
|
||||||
|
data.Exited++
|
||||||
|
case "removing":
|
||||||
|
data.Removing++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error) {
|
func (u *ContainerService) ContainerListStats() ([]dto.ContainerListStats, error) {
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
|
||||||
baRouter.POST("/info", baseApi.ContainerInfo)
|
baRouter.POST("/info", baseApi.ContainerInfo)
|
||||||
baRouter.POST("/search", baseApi.SearchContainer)
|
baRouter.POST("/search", baseApi.SearchContainer)
|
||||||
baRouter.POST("/list", baseApi.ListContainer)
|
baRouter.POST("/list", baseApi.ListContainer)
|
||||||
|
baRouter.GET("/status", baseApi.LoadContainerStatus)
|
||||||
baRouter.GET("/list/stats", baseApi.ContainerListStats)
|
baRouter.GET("/list/stats", baseApi.ContainerListStats)
|
||||||
baRouter.GET("/search/log", baseApi.ContainerLogs)
|
baRouter.GET("/search/log", baseApi.ContainerLogs)
|
||||||
baRouter.POST("/download/log", baseApi.DownloadContainerLogs)
|
baRouter.POST("/download/log", baseApi.DownloadContainerLogs)
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,16 @@ export namespace Container {
|
||||||
orderBy: string;
|
orderBy: string;
|
||||||
order: string;
|
order: string;
|
||||||
}
|
}
|
||||||
|
export interface ContainerStatus {
|
||||||
|
all: number;
|
||||||
|
created: number;
|
||||||
|
running: number;
|
||||||
|
paused: number;
|
||||||
|
restarting: number;
|
||||||
|
removing: number;
|
||||||
|
exited: number;
|
||||||
|
dead: number;
|
||||||
|
}
|
||||||
export interface ResourceLimit {
|
export interface ResourceLimit {
|
||||||
cpu: number;
|
cpu: number;
|
||||||
memory: number;
|
memory: number;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,9 @@ export const searchContainer = (params: Container.ContainerSearch) => {
|
||||||
export const listContainer = () => {
|
export const listContainer = () => {
|
||||||
return http.post<Array<string>>(`/containers/list`, {});
|
return http.post<Array<string>>(`/containers/list`, {});
|
||||||
};
|
};
|
||||||
|
export const loadContainerStatus = () => {
|
||||||
|
return http.get<Container.ContainerStatus>(`/containers/status`);
|
||||||
|
};
|
||||||
export const loadResourceLimit = () => {
|
export const loadResourceLimit = () => {
|
||||||
return http.get<Container.ResourceLimit>(`/containers/limit`);
|
return http.get<Container.ResourceLimit>(`/containers/limit`);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,7 @@ const message = {
|
||||||
host: '主机',
|
host: '主机',
|
||||||
files: '文件',
|
files: '文件',
|
||||||
monitor: '监控',
|
monitor: '监控',
|
||||||
terminal: '终端',
|
terminal: 'WEB终端',
|
||||||
settings: '面板设置',
|
settings: '面板设置',
|
||||||
toolbox: '工具箱',
|
toolbox: '工具箱',
|
||||||
logs: '日志审计',
|
logs: '日志审计',
|
||||||
|
|
@ -1137,7 +1137,7 @@ const message = {
|
||||||
role: '权限',
|
role: '权限',
|
||||||
info: '属性',
|
info: '属性',
|
||||||
linkFile: '软连接文件',
|
linkFile: '软连接文件',
|
||||||
terminal: 'Web终端',
|
terminal: '终端',
|
||||||
shareList: '分享列表',
|
shareList: '分享列表',
|
||||||
zip: '压缩',
|
zip: '压缩',
|
||||||
group: '用户组',
|
group: '用户组',
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,17 @@ const containerRouter = {
|
||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'container/operate',
|
||||||
|
name: 'ContainerCreate',
|
||||||
|
component: () => import('@/views/container/container/operate/index.vue'),
|
||||||
|
props: true,
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/containers',
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'composeDetail/:filters?',
|
path: 'composeDetail/:filters?',
|
||||||
name: 'ComposeDetail',
|
name: 'ComposeDetail',
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,73 @@
|
||||||
<el-button type="primary" class="bt" link @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
<el-button type="primary" class="bt" link @click="goSetting">【 {{ $t('container.setting') }} 】</el-button>
|
||||||
<span>{{ $t('container.startIn') }}</span>
|
<span>{{ $t('container.startIn') }}</span>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
<div class="mt-5">
|
||||||
|
<el-tag @click="searchWithStatus('all')" v-if="countItem.all" effect="plain" size="large">
|
||||||
|
{{ $t('commons.table.all') }} * {{ countItem.all }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
@click="searchWithStatus('running')"
|
||||||
|
v-if="countItem.running"
|
||||||
|
effect="plain"
|
||||||
|
size="large"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.running') }} * {{ countItem.running }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
@click="searchWithStatus('created')"
|
||||||
|
v-if="countItem.created"
|
||||||
|
effect="plain"
|
||||||
|
size="large"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.created') }} * {{ countItem.created }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
@click="searchWithStatus('paused')"
|
||||||
|
v-if="countItem.paused"
|
||||||
|
effect="plain"
|
||||||
|
size="large"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.paused') }} * {{ countItem.paused }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
@click="searchWithStatus('restarting')"
|
||||||
|
v-if="countItem.restarting"
|
||||||
|
effect="plain"
|
||||||
|
size="large"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.restarting') }} * {{ countItem.restarting }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
@click="searchWithStatus('removing')"
|
||||||
|
v-if="countItem.removing"
|
||||||
|
effect="plain"
|
||||||
|
size="large"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.removing') }} * {{ countItem.removing }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag
|
||||||
|
@click="searchWithStatus('exited')"
|
||||||
|
v-if="countItem.exited"
|
||||||
|
effect="plain"
|
||||||
|
size="large"
|
||||||
|
class="ml-2"
|
||||||
|
>
|
||||||
|
{{ $t('commons.status.exited') }} * {{ countItem.exited }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag @click="searchWithStatus('dead')" v-if="countItem.dead" effect="plain" size="large" class="ml-2">
|
||||||
|
{{ $t('commons.status.dead') }} * {{ countItem.dead }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
|
||||||
<LayoutContent :title="$t('container.container')" :class="{ mask: dockerStatus != 'Running' }">
|
<LayoutContent :title="$t('container.container')" :class="{ mask: dockerStatus != 'Running' }">
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-button type="primary" @click="onOpenDialog('create')">
|
<el-button type="primary" @click="onContainerOperate('')">
|
||||||
{{ $t('container.create') }}
|
{{ $t('container.create') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" plain @click="onClean()">
|
<el-button type="primary" plain @click="onClean()">
|
||||||
|
|
@ -41,17 +105,6 @@
|
||||||
<el-checkbox v-model="includeAppStore" @change="search()" class="!mr-2.5">
|
<el-checkbox v-model="includeAppStore" @change="search()" class="!mr-2.5">
|
||||||
{{ $t('container.includeAppstore') }}
|
{{ $t('container.includeAppstore') }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<el-select v-model="searchState" @change="search()" clearable class="p-w-200 mr-2.5">
|
|
||||||
<template #prefix>{{ $t('commons.table.status') }}</template>
|
|
||||||
<el-option :label="$t('commons.table.all')" value="all"></el-option>
|
|
||||||
<el-option :label="$t('commons.status.created')" value="created"></el-option>
|
|
||||||
<el-option :label="$t('commons.status.running')" value="running"></el-option>
|
|
||||||
<el-option :label="$t('commons.status.paused')" value="paused"></el-option>
|
|
||||||
<el-option :label="$t('commons.status.restarting')" value="restarting"></el-option>
|
|
||||||
<el-option :label="$t('commons.status.removing')" value="removing"></el-option>
|
|
||||||
<el-option :label="$t('commons.status.exited')" value="exited"></el-option>
|
|
||||||
<el-option :label="$t('commons.status.dead')" value="dead"></el-option>
|
|
||||||
</el-select>
|
|
||||||
<TableSearch @search="search()" v-model:searchName="searchName" class="mr-2.5" />
|
<TableSearch @search="search()" v-model:searchName="searchName" class="mr-2.5" />
|
||||||
<TableSetting title="container-refresh" @search="refresh()" class="mr-2.5" />
|
<TableSetting title="container-refresh" @search="refresh()" class="mr-2.5" />
|
||||||
<fu-table-column-select
|
<fu-table-column-select
|
||||||
|
|
@ -98,9 +151,51 @@
|
||||||
min-width="150"
|
min-width="150"
|
||||||
prop="imageName"
|
prop="imageName"
|
||||||
/>
|
/>
|
||||||
<el-table-column :label="$t('commons.table.status')" min-width="100" prop="state" sortable>
|
<el-table-column :label="$t('commons.table.status')" min-width="100" prop="state">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<Status :key="row.state" :status="row.state"></Status>
|
<el-dropdown placement="bottom">
|
||||||
|
<Status :key="row.state" :status="row.state"></Status>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item
|
||||||
|
:disabled="checkStatus('start', row)"
|
||||||
|
@click="onOperate('start', row)"
|
||||||
|
>
|
||||||
|
{{ $t('container.start') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:disabled="checkStatus('stop', row)"
|
||||||
|
@click="onOperate('stop', row)"
|
||||||
|
>
|
||||||
|
{{ $t('container.stop') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:disabled="checkStatus('restart', row)"
|
||||||
|
@click="onOperate('restart', row)"
|
||||||
|
>
|
||||||
|
{{ $t('container.restart') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:disabled="checkStatus('kill', row)"
|
||||||
|
@click="onOperate('kill', row)"
|
||||||
|
>
|
||||||
|
{{ $t('container.kill') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:disabled="checkStatus('pause', row)"
|
||||||
|
@click="onOperate('pause', row)"
|
||||||
|
>
|
||||||
|
{{ $t('container.pause') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
:disabled="checkStatus('unpause', row)"
|
||||||
|
@click="onOperate('unpause', row)"
|
||||||
|
>
|
||||||
|
{{ $t('container.unpause') }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
|
|
@ -299,7 +394,6 @@
|
||||||
|
|
||||||
<RenameDialog @search="search" ref="dialogRenameRef" />
|
<RenameDialog @search="search" ref="dialogRenameRef" />
|
||||||
<ContainerLogDialog ref="dialogContainerLogRef" />
|
<ContainerLogDialog ref="dialogContainerLogRef" />
|
||||||
<OperateDialog @search="search" ref="dialogOperateRef" />
|
|
||||||
<UpgradeDialog @search="search" ref="dialogUpgradeRef" />
|
<UpgradeDialog @search="search" ref="dialogUpgradeRef" />
|
||||||
<CommitDialog @search="search" ref="dialogCommitRef" />
|
<CommitDialog @search="search" ref="dialogCommitRef" />
|
||||||
<MonitorDialog ref="dialogMonitorRef" />
|
<MonitorDialog ref="dialogMonitorRef" />
|
||||||
|
|
@ -312,7 +406,6 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import PruneDialog from '@/views/container/container/prune/index.vue';
|
import PruneDialog from '@/views/container/container/prune/index.vue';
|
||||||
import RenameDialog from '@/views/container/container/rename/index.vue';
|
import RenameDialog from '@/views/container/container/rename/index.vue';
|
||||||
import OperateDialog from '@/views/container/container/operate/index.vue';
|
|
||||||
import UpgradeDialog from '@/views/container/container/upgrade/index.vue';
|
import UpgradeDialog from '@/views/container/container/upgrade/index.vue';
|
||||||
import CommitDialog from '@/views/container/container/commit/index.vue';
|
import CommitDialog from '@/views/container/container/commit/index.vue';
|
||||||
import MonitorDialog from '@/views/container/container/monitor/index.vue';
|
import MonitorDialog from '@/views/container/container/monitor/index.vue';
|
||||||
|
|
@ -326,7 +419,7 @@ import {
|
||||||
containerListStats,
|
containerListStats,
|
||||||
containerOperator,
|
containerOperator,
|
||||||
inspect,
|
inspect,
|
||||||
loadContainerInfo,
|
loadContainerStatus,
|
||||||
loadDockerStatus,
|
loadDockerStatus,
|
||||||
searchContainer,
|
searchContainer,
|
||||||
} from '@/api/modules/container';
|
} from '@/api/modules/container';
|
||||||
|
|
@ -349,11 +442,11 @@ const paginationConfig = reactive({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
total: 0,
|
total: 0,
|
||||||
|
state: 'all',
|
||||||
orderBy: 'created_at',
|
orderBy: 'created_at',
|
||||||
order: 'null',
|
order: 'null',
|
||||||
});
|
});
|
||||||
const searchName = ref();
|
const searchName = ref();
|
||||||
const searchState = ref('all');
|
|
||||||
const dialogUpgradeRef = ref();
|
const dialogUpgradeRef = ref();
|
||||||
const dialogCommitRef = ref();
|
const dialogCommitRef = ref();
|
||||||
const dialogPortJumpRef = ref();
|
const dialogPortJumpRef = ref();
|
||||||
|
|
@ -361,6 +454,17 @@ const opRef = ref();
|
||||||
const includeAppStore = ref(true);
|
const includeAppStore = ref(true);
|
||||||
const columns = ref([]);
|
const columns = ref([]);
|
||||||
|
|
||||||
|
const countItem = reactive({
|
||||||
|
all: 0,
|
||||||
|
created: 0,
|
||||||
|
running: 0,
|
||||||
|
paused: 0,
|
||||||
|
restarting: 0,
|
||||||
|
removing: 0,
|
||||||
|
exited: 0,
|
||||||
|
dead: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const dockerStatus = ref('Running');
|
const dockerStatus = ref('Running');
|
||||||
const loadStatus = async () => {
|
const loadStatus = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
@ -417,7 +521,7 @@ const search = async (column?: any) => {
|
||||||
paginationConfig.order = column?.order ? column.order : paginationConfig.order;
|
paginationConfig.order = column?.order ? column.order : paginationConfig.order;
|
||||||
let params = {
|
let params = {
|
||||||
name: searchName.value,
|
name: searchName.value,
|
||||||
state: searchState.value || 'all',
|
state: paginationConfig.state || 'all',
|
||||||
page: paginationConfig.currentPage,
|
page: paginationConfig.currentPage,
|
||||||
pageSize: paginationConfig.pageSize,
|
pageSize: paginationConfig.pageSize,
|
||||||
filters: filterItem,
|
filters: filterItem,
|
||||||
|
|
@ -427,6 +531,7 @@ const search = async (column?: any) => {
|
||||||
};
|
};
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
loadStats();
|
loadStats();
|
||||||
|
loadContainerCount();
|
||||||
await searchContainer(params)
|
await searchContainer(params)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
@ -438,11 +543,29 @@ const search = async (column?: any) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const searchWithStatus = (item: any) => {
|
||||||
|
paginationConfig.state = item;
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadContainerCount = async () => {
|
||||||
|
await loadContainerStatus().then((res) => {
|
||||||
|
countItem.all = res.data.all;
|
||||||
|
countItem.running = res.data.running;
|
||||||
|
countItem.paused = res.data.paused;
|
||||||
|
countItem.restarting = res.data.restarting;
|
||||||
|
countItem.removing = res.data.removing;
|
||||||
|
countItem.created = res.data.created;
|
||||||
|
countItem.dead = res.data.dead;
|
||||||
|
countItem.exited = res.data.exited;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const refresh = async () => {
|
const refresh = async () => {
|
||||||
let filterItem = props.filters ? props.filters : '';
|
let filterItem = props.filters ? props.filters : '';
|
||||||
let params = {
|
let params = {
|
||||||
name: searchName.value,
|
name: searchName.value,
|
||||||
state: searchState.value || 'all',
|
state: paginationConfig.state || 'all',
|
||||||
page: paginationConfig.currentPage,
|
page: paginationConfig.currentPage,
|
||||||
pageSize: paginationConfig.pageSize,
|
pageSize: paginationConfig.pageSize,
|
||||||
filters: filterItem,
|
filters: filterItem,
|
||||||
|
|
@ -522,35 +645,8 @@ function loadMemValue(t: number) {
|
||||||
return Number((t / Math.pow(num, 3)).toFixed(2));
|
return Number((t / Math.pow(num, 3)).toFixed(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
const dialogOperateRef = ref();
|
const onContainerOperate = async (containerID: string) => {
|
||||||
const onEdit = async (container: string) => {
|
router.push({ name: 'ContainerCreate', query: { containerID: containerID } });
|
||||||
const res = await loadContainerInfo(container);
|
|
||||||
if (res.data) {
|
|
||||||
onOpenDialog('edit', res.data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const onOpenDialog = async (
|
|
||||||
title: string,
|
|
||||||
rowData: Partial<Container.ContainerHelper> = {
|
|
||||||
cmd: [],
|
|
||||||
cmdStr: '',
|
|
||||||
publishAllPorts: false,
|
|
||||||
exposedPorts: [],
|
|
||||||
cpuShares: 1024,
|
|
||||||
nanoCPUs: 0,
|
|
||||||
memory: 0,
|
|
||||||
memoryItem: 0,
|
|
||||||
volumes: [],
|
|
||||||
labels: [],
|
|
||||||
env: [],
|
|
||||||
restartPolicy: 'no',
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let params = {
|
|
||||||
title,
|
|
||||||
rowData: { ...rowData },
|
|
||||||
};
|
|
||||||
dialogOperateRef.value!.acceptParams(params);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const dialogMonitorRef = ref();
|
const dialogMonitorRef = ref();
|
||||||
|
|
@ -654,7 +750,7 @@ const buttons = [
|
||||||
{
|
{
|
||||||
label: i18n.global.t('commons.button.edit'),
|
label: i18n.global.t('commons.button.edit'),
|
||||||
click: (row: Container.ContainerInfo) => {
|
click: (row: Container.ContainerInfo) => {
|
||||||
onEdit(row.containerID);
|
onContainerOperate(row.containerID);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -690,60 +786,6 @@ const buttons = [
|
||||||
return checkStatus('commit', row);
|
return checkStatus('commit', row);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: i18n.global.t('container.start'),
|
|
||||||
click: (row: Container.ContainerInfo) => {
|
|
||||||
onOperate('start', row);
|
|
||||||
},
|
|
||||||
disabled: (row: any) => {
|
|
||||||
return checkStatus('start', row);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.global.t('container.stop'),
|
|
||||||
click: (row: Container.ContainerInfo) => {
|
|
||||||
onOperate('stop', row);
|
|
||||||
},
|
|
||||||
disabled: (row: any) => {
|
|
||||||
return checkStatus('stop', row);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.global.t('container.restart'),
|
|
||||||
click: (row: Container.ContainerInfo) => {
|
|
||||||
onOperate('restart', row);
|
|
||||||
},
|
|
||||||
disabled: (row: any) => {
|
|
||||||
return checkStatus('restart', row);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.global.t('container.kill'),
|
|
||||||
click: (row: Container.ContainerInfo) => {
|
|
||||||
onOperate('kill', row);
|
|
||||||
},
|
|
||||||
disabled: (row: any) => {
|
|
||||||
return checkStatus('kill', row);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.global.t('container.pause'),
|
|
||||||
click: (row: Container.ContainerInfo) => {
|
|
||||||
onOperate('pause', row);
|
|
||||||
},
|
|
||||||
disabled: (row: any) => {
|
|
||||||
return checkStatus('pause', row);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.global.t('container.unpause'),
|
|
||||||
click: (row: Container.ContainerInfo) => {
|
|
||||||
onOperate('unpause', row);
|
|
||||||
},
|
|
||||||
disabled: (row: any) => {
|
|
||||||
return checkStatus('unpause', row);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: i18n.global.t('container.remove'),
|
label: i18n.global.t('container.remove'),
|
||||||
click: (row: Container.ContainerInfo) => {
|
click: (row: Container.ContainerInfo) => {
|
||||||
|
|
|
||||||
|
|
@ -1,252 +1,286 @@
|
||||||
<template>
|
<template>
|
||||||
<DrawerPro
|
<div>
|
||||||
v-model="drawerVisible"
|
<LayoutContent :title="isCreate ? $t('container.create') : $t('commons.button.edit') + ' - ' + form.name">
|
||||||
:header="title"
|
<template #prompt>
|
||||||
:back="handleClose"
|
<el-alert
|
||||||
:resource="dialogData.title === 'create' ? '' : dialogData.rowData?.name"
|
v-if="!isCreate && isFromApp(form)"
|
||||||
size="large"
|
:title="$t('container.containerFromAppHelper')"
|
||||||
>
|
:closable="false"
|
||||||
<el-form
|
type="error"
|
||||||
ref="formRef"
|
|
||||||
label-position="top"
|
|
||||||
v-loading="loading"
|
|
||||||
:model="dialogData.rowData!"
|
|
||||||
:rules="rules"
|
|
||||||
label-width="80px"
|
|
||||||
>
|
|
||||||
<el-alert
|
|
||||||
v-if="dialogData.title === 'edit' && isFromApp(dialogData.rowData!)"
|
|
||||||
:title="$t('container.containerFromAppHelper')"
|
|
||||||
:closable="false"
|
|
||||||
type="error"
|
|
||||||
/>
|
|
||||||
<el-form-item class="mt-5" :label="$t('commons.table.name')" prop="name">
|
|
||||||
<el-input
|
|
||||||
:disabled="isFromApp(dialogData.rowData!)"
|
|
||||||
clearable
|
|
||||||
v-model.trim="dialogData.rowData!.name"
|
|
||||||
/>
|
/>
|
||||||
<div v-if="dialogData.title === 'edit' && isFromApp(dialogData.rowData!)">
|
</template>
|
||||||
<span class="input-help">
|
<template #main>
|
||||||
{{ $t('container.containerFromAppHelper1') }}
|
<el-form
|
||||||
<el-button style="margin-left: -5px" size="small" text type="primary" @click="goRouter()">
|
ref="formRef"
|
||||||
<el-icon><Position /></el-icon>
|
label-position="top"
|
||||||
{{ $t('firewall.quickJump') }}
|
v-loading="loading"
|
||||||
</el-button>
|
:model="form"
|
||||||
</span>
|
:rules="rules"
|
||||||
</div>
|
label-width="80px"
|
||||||
</el-form-item>
|
>
|
||||||
<el-form-item :label="$t('container.image')" prop="image">
|
<el-row>
|
||||||
<el-checkbox v-model="dialogData.rowData!.imageInput" :label="$t('container.input')" />
|
<el-col :span="1"><br /></el-col>
|
||||||
<el-select v-if="!dialogData.rowData!.imageInput" filterable v-model="dialogData.rowData!.image">
|
<el-col :xs="24" :sm="20" :md="15" :lg="12" :xl="12">
|
||||||
<el-option v-for="(item, index) of images" :key="index" :value="item.option" :label="item.option" />
|
<el-form-item class="mt-5" :label="$t('commons.table.name')" prop="name">
|
||||||
</el-select>
|
<el-input :disabled="isFromApp(form)" clearable v-model.trim="form.name" />
|
||||||
<el-input v-else v-model="dialogData.rowData!.image" />
|
<div v-if="!isCreate && isFromApp(form)">
|
||||||
</el-form-item>
|
<span class="input-help">
|
||||||
<el-form-item prop="forcePull">
|
{{ $t('container.containerFromAppHelper1') }}
|
||||||
<el-checkbox v-model="dialogData.rowData!.forcePull">
|
<el-button
|
||||||
{{ $t('container.forcePull') }}
|
style="margin-left: -5px"
|
||||||
</el-checkbox>
|
size="small"
|
||||||
<span class="input-help">{{ $t('container.forcePullHelper') }}</span>
|
text
|
||||||
</el-form-item>
|
type="primary"
|
||||||
<el-form-item :label="$t('commons.table.port')">
|
@click="goRouter()"
|
||||||
<el-radio-group v-model="dialogData.rowData!.publishAllPorts" class="ml-4">
|
>
|
||||||
<el-radio :value="false">{{ $t('container.exposePort') }}</el-radio>
|
<el-icon><Position /></el-icon>
|
||||||
<el-radio :value="true">{{ $t('container.exposeAll') }}</el-radio>
|
{{ $t('firewall.quickJump') }}
|
||||||
</el-radio-group>
|
</el-button>
|
||||||
</el-form-item>
|
</span>
|
||||||
<el-form-item v-if="!dialogData.rowData!.publishAllPorts">
|
</div>
|
||||||
<el-card class="widthClass">
|
</el-form-item>
|
||||||
<el-table
|
<el-form-item :label="$t('container.image')" prop="image">
|
||||||
v-if="dialogData.rowData!.exposedPorts.length !== 0"
|
<el-checkbox v-model="form.imageInput" :label="$t('container.input')" />
|
||||||
:data="dialogData.rowData!.exposedPorts"
|
<el-select v-if="!form.imageInput" filterable v-model="form.image">
|
||||||
>
|
<el-option
|
||||||
<el-table-column :label="$t('container.server')" min-width="150">
|
v-for="(item, index) of images"
|
||||||
<template #default="{ row }">
|
:key="index"
|
||||||
<el-input :placeholder="$t('container.serverExample')" v-model="row.host" />
|
:value="item.option"
|
||||||
</template>
|
:label="item.option"
|
||||||
</el-table-column>
|
/>
|
||||||
<el-table-column :label="$t('container.container')" min-width="80">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input :placeholder="$t('container.containerExample')" v-model="row.containerPort" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="$t('commons.table.protocol')" min-width="50">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-select
|
|
||||||
v-model="row.protocol"
|
|
||||||
style="width: 100%"
|
|
||||||
:placeholder="$t('container.serverExample')"
|
|
||||||
>
|
|
||||||
<el-option label="tcp" value="tcp" />
|
|
||||||
<el-option label="udp" value="udp" />
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</template>
|
<el-input v-else v-model="form.image" />
|
||||||
</el-table-column>
|
</el-form-item>
|
||||||
<el-table-column min-width="35">
|
<el-form-item prop="forcePull">
|
||||||
<template #default="scope">
|
<el-checkbox v-model="form.forcePull">
|
||||||
<el-button link type="primary" @click="handlePortsDelete(scope.$index)">
|
{{ $t('container.forcePull') }}
|
||||||
{{ $t('commons.button.delete') }}
|
</el-checkbox>
|
||||||
|
<span class="input-help">{{ $t('container.forcePullHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.table.port')">
|
||||||
|
<el-radio-group v-model="form.publishAllPorts" class="ml-4">
|
||||||
|
<el-radio :value="false">{{ $t('container.exposePort') }}</el-radio>
|
||||||
|
<el-radio :value="true">{{ $t('container.exposeAll') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="!form.publishAllPorts">
|
||||||
|
<el-card class="widthClass">
|
||||||
|
<el-table v-if="form.exposedPorts.length !== 0" :data="form.exposedPorts">
|
||||||
|
<el-table-column :label="$t('container.server')" min-width="150">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input
|
||||||
|
:placeholder="$t('container.serverExample')"
|
||||||
|
v-model="row.host"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('container.container')" min-width="80">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-input
|
||||||
|
:placeholder="$t('container.containerExample')"
|
||||||
|
v-model="row.containerPort"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('commons.table.protocol')" min-width="50">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-select
|
||||||
|
v-model="row.protocol"
|
||||||
|
style="width: 100%"
|
||||||
|
:placeholder="$t('container.serverExample')"
|
||||||
|
>
|
||||||
|
<el-option label="tcp" value="tcp" />
|
||||||
|
<el-option label="udp" value="udp" />
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column min-width="35">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" @click="handlePortsDelete(scope.$index)">
|
||||||
|
{{ $t('commons.button.delete') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-button class="ml-3 mt-2" @click="handlePortsAdd()">
|
||||||
|
{{ $t('commons.button.add') }}
|
||||||
|
</el-button>
|
||||||
|
</el-card>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.network')" prop="network">
|
||||||
|
<el-select v-model="form.network">
|
||||||
|
<el-option
|
||||||
|
v-for="(item, indexV) of networks"
|
||||||
|
:key="indexV"
|
||||||
|
:value="item.option"
|
||||||
|
:label="item.option"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="ipv4" prop="ipv4">
|
||||||
|
<el-input v-model="form.ipv4" :placeholder="$t('container.inputIpv4')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="ipv6" prop="ipv6">
|
||||||
|
<el-input v-model="form.ipv6" :placeholder="$t('container.inputIpv6')" />
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="$t('container.mount')">
|
||||||
|
<div v-for="(row, index) in form.volumes" :key="index" style="width: 100%">
|
||||||
|
<el-card class="mt-1">
|
||||||
|
<el-radio-group v-model="row.type">
|
||||||
|
<el-radio-button value="volume">
|
||||||
|
{{ $t('container.volumeOption') }}
|
||||||
|
</el-radio-button>
|
||||||
|
<el-radio-button value="bind">
|
||||||
|
{{ $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.type === 'volume'"
|
||||||
|
: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-button>
|
||||||
</template>
|
</el-form-item>
|
||||||
</el-table-column>
|
<el-form-item label="Command" prop="cmdStr">
|
||||||
</el-table>
|
<el-input v-model="form.cmdStr" :placeholder="$t('container.cmdHelper')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Entrypoint" prop="entrypointStr">
|
||||||
|
<el-input
|
||||||
|
v-model="form.entrypointStr"
|
||||||
|
:placeholder="$t('container.entrypointHelper')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item prop="autoRemove">
|
||||||
|
<el-checkbox v-model="form.autoRemove">
|
||||||
|
{{ $t('container.autoRemove') }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="form.privileged">
|
||||||
|
{{ $t('container.privileged') }}
|
||||||
|
</el-checkbox>
|
||||||
|
<span class="input-help">{{ $t('container.privilegedHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.console')">
|
||||||
|
<el-checkbox v-model="form.tty">{{ $t('container.tty') }}</el-checkbox>
|
||||||
|
<el-checkbox v-model="form.openStdin">
|
||||||
|
{{ $t('container.openStdin') }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.restartPolicy')" prop="restartPolicy">
|
||||||
|
<el-radio-group v-model="form.restartPolicy">
|
||||||
|
<el-radio value="no">{{ $t('container.no') }}</el-radio>
|
||||||
|
<el-radio value="always">{{ $t('container.always') }}</el-radio>
|
||||||
|
<el-radio value="on-failure">{{ $t('container.onFailure') }}</el-radio>
|
||||||
|
<el-radio value="unless-stopped">{{ $t('container.unlessStopped') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.cpuShare')" prop="cpuShares">
|
||||||
|
<el-input class="mini-form-item" v-model.number="form.cpuShares" />
|
||||||
|
<span class="input-help">{{ $t('container.cpuShareHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="$t('container.cpuQuota')"
|
||||||
|
prop="nanoCPUs"
|
||||||
|
:rules="checkFloatNumberRange(0, Number(limits.cpu))"
|
||||||
|
>
|
||||||
|
<el-input class="mini-form-item" v-model="form.nanoCPUs">
|
||||||
|
<template #append>
|
||||||
|
<div style="width: 35px">{{ $t('commons.units.core') }}</div>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('container.limitHelper', [limits.cpu]) }}{{ $t('commons.units.core') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
:label="$t('container.memoryLimit')"
|
||||||
|
prop="memory"
|
||||||
|
:rules="checkFloatNumberRange(0, Number(limits.memory))"
|
||||||
|
>
|
||||||
|
<el-input class="mini-form-item" v-model="form.memory">
|
||||||
|
<template #append><div style="width: 35px">MB</div></template>
|
||||||
|
</el-input>
|
||||||
|
<span class="input-help">{{ $t('container.limitHelper', [limits.memory]) }}MB</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.tag')" prop="labelsStr">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:placeholder="$t('container.tagHelper')"
|
||||||
|
:rows="3"
|
||||||
|
v-model="form.labelsStr"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.env')" prop="envStr">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:placeholder="$t('container.tagHelper')"
|
||||||
|
:rows="3"
|
||||||
|
v-model="form.envStr"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
<el-button class="ml-3 mt-2" @click="handlePortsAdd()">
|
<el-button :disabled="loading" @click="goBack">
|
||||||
{{ $t('commons.button.add') }}
|
{{ $t('commons.button.back') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-card>
|
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
||||||
</el-form-item>
|
{{ $t('commons.button.confirm') }}
|
||||||
<el-form-item :label="$t('container.network')" prop="network">
|
</el-button>
|
||||||
<el-select v-model="dialogData.rowData!.network">
|
</el-col>
|
||||||
<el-option
|
</el-row>
|
||||||
v-for="(item, indexV) of networks"
|
</el-form>
|
||||||
:key="indexV"
|
</template>
|
||||||
:value="item.option"
|
</LayoutContent>
|
||||||
:label="item.option"
|
</div>
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item label="ipv4" prop="ipv4">
|
|
||||||
<el-input v-model="dialogData.rowData!.ipv4" :placeholder="$t('container.inputIpv4')" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="ipv6" prop="ipv6">
|
|
||||||
<el-input v-model="dialogData.rowData!.ipv6" :placeholder="$t('container.inputIpv6')" />
|
|
||||||
</el-form-item>
|
|
||||||
|
|
||||||
<el-form-item :label="$t('container.mount')">
|
|
||||||
<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.type">
|
|
||||||
<el-radio-button value="volume">{{ $t('container.volumeOption') }}</el-radio-button>
|
|
||||||
<el-radio-button value="bind">{{ $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.type === 'volume'" :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')" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="Entrypoint" prop="entrypointStr">
|
|
||||||
<el-input v-model="dialogData.rowData!.entrypointStr" :placeholder="$t('container.entrypointHelper')" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item prop="autoRemove">
|
|
||||||
<el-checkbox v-model="dialogData.rowData!.autoRemove">
|
|
||||||
{{ $t('container.autoRemove') }}
|
|
||||||
</el-checkbox>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item>
|
|
||||||
<el-checkbox v-model="dialogData.rowData!.privileged">
|
|
||||||
{{ $t('container.privileged') }}
|
|
||||||
</el-checkbox>
|
|
||||||
<span class="input-help">{{ $t('container.privilegedHelper') }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('container.console')">
|
|
||||||
<el-checkbox v-model="dialogData.rowData!.tty">{{ $t('container.tty') }}</el-checkbox>
|
|
||||||
<el-checkbox v-model="dialogData.rowData!.openStdin">
|
|
||||||
{{ $t('container.openStdin') }}
|
|
||||||
</el-checkbox>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('container.restartPolicy')" prop="restartPolicy">
|
|
||||||
<el-radio-group v-model="dialogData.rowData!.restartPolicy">
|
|
||||||
<el-radio value="no">{{ $t('container.no') }}</el-radio>
|
|
||||||
<el-radio value="always">{{ $t('container.always') }}</el-radio>
|
|
||||||
<el-radio value="on-failure">{{ $t('container.onFailure') }}</el-radio>
|
|
||||||
<el-radio value="unless-stopped">{{ $t('container.unlessStopped') }}</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('container.cpuShare')" prop="cpuShares">
|
|
||||||
<el-input class="mini-form-item" v-model.number="dialogData.rowData!.cpuShares" />
|
|
||||||
<span class="input-help">{{ $t('container.cpuShareHelper') }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('container.cpuQuota')"
|
|
||||||
prop="nanoCPUs"
|
|
||||||
:rules="checkFloatNumberRange(0, Number(limits.cpu))"
|
|
||||||
>
|
|
||||||
<el-input class="mini-form-item" v-model="dialogData.rowData!.nanoCPUs">
|
|
||||||
<template #append>
|
|
||||||
<div style="width: 35px">{{ $t('commons.units.core') }}</div>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
<span class="input-help">
|
|
||||||
{{ $t('container.limitHelper', [limits.cpu]) }}{{ $t('commons.units.core') }}
|
|
||||||
</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item
|
|
||||||
:label="$t('container.memoryLimit')"
|
|
||||||
prop="memory"
|
|
||||||
:rules="checkFloatNumberRange(0, Number(limits.memory))"
|
|
||||||
>
|
|
||||||
<el-input class="mini-form-item" v-model="dialogData.rowData!.memory">
|
|
||||||
<template #append><div style="width: 35px">MB</div></template>
|
|
||||||
</el-input>
|
|
||||||
<span class="input-help">{{ $t('container.limitHelper', [limits.memory]) }}MB</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('container.tag')" prop="labelsStr">
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
:placeholder="$t('container.tagHelper')"
|
|
||||||
:rows="3"
|
|
||||||
v-model="dialogData.rowData!.labelsStr"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('container.env')" prop="envStr">
|
|
||||||
<el-input
|
|
||||||
type="textarea"
|
|
||||||
:placeholder="$t('container.tagHelper')"
|
|
||||||
:rows="3"
|
|
||||||
v-model="dialogData.rowData!.envStr"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<span class="dialog-footer">
|
|
||||||
<el-button :disabled="loading" @click="drawerVisible = false">
|
|
||||||
{{ $t('commons.button.cancel') }}
|
|
||||||
</el-button>
|
|
||||||
<el-button :disabled="loading" type="primary" @click="onSubmit(formRef)">
|
|
||||||
{{ $t('commons.button.confirm') }}
|
|
||||||
</el-button>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</DrawerPro>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
@ -262,6 +296,7 @@ import {
|
||||||
loadResourceLimit,
|
loadResourceLimit,
|
||||||
listNetwork,
|
listNetwork,
|
||||||
searchContainer,
|
searchContainer,
|
||||||
|
loadContainerInfo,
|
||||||
} from '@/api/modules/container';
|
} from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||||
|
|
@ -269,61 +304,90 @@ import { checkIpV4V6, checkPort } from '@/utils/util';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
interface DialogProps {
|
const isCreate = ref();
|
||||||
title: string;
|
const form = reactive<Container.ContainerHelper>({
|
||||||
rowData?: Container.ContainerHelper;
|
containerID: '',
|
||||||
getTableList?: () => Promise<any>;
|
name: '',
|
||||||
}
|
image: '',
|
||||||
|
imageInput: false,
|
||||||
const title = ref<string>('');
|
forcePull: false,
|
||||||
const drawerVisible = ref(false);
|
network: '',
|
||||||
|
ipv4: '',
|
||||||
const dialogData = ref<DialogProps>({
|
ipv6: '',
|
||||||
title: '',
|
cmdStr: '',
|
||||||
|
entrypointStr: '',
|
||||||
|
memoryItem: 0,
|
||||||
|
cmd: [],
|
||||||
|
openStdin: false,
|
||||||
|
tty: false,
|
||||||
|
entrypoint: [],
|
||||||
|
publishAllPorts: false,
|
||||||
|
exposedPorts: [],
|
||||||
|
nanoCPUs: 0,
|
||||||
|
cpuShares: 1024,
|
||||||
|
memory: 0,
|
||||||
|
volumes: [],
|
||||||
|
privileged: false,
|
||||||
|
autoRemove: false,
|
||||||
|
labels: [],
|
||||||
|
labelsStr: '',
|
||||||
|
env: [],
|
||||||
|
envStr: '',
|
||||||
|
restartPolicy: 'no',
|
||||||
});
|
});
|
||||||
const acceptParams = (params: DialogProps): void => {
|
const search = async () => {
|
||||||
dialogData.value = params;
|
if (!isCreate.value) {
|
||||||
title.value = i18n.global.t('container.' + dialogData.value.title);
|
const res = await loadContainerInfo(form.containerID);
|
||||||
if (params.title === 'edit') {
|
if (res.data) {
|
||||||
dialogData.value.rowData.memory = Number(dialogData.value.rowData.memory.toFixed(2));
|
form.name = res.data.name;
|
||||||
dialogData.value.rowData.cmd = dialogData.value.rowData.cmd || [];
|
form.image = res.data.image;
|
||||||
let itemCmd = '';
|
form.network = res.data.network;
|
||||||
for (const item of dialogData.value.rowData.cmd) {
|
form.ipv4 = res.data.ipv4;
|
||||||
itemCmd += `'${item}' `;
|
form.ipv6 = res.data.ipv6;
|
||||||
}
|
form.openStdin = res.data.openStdin;
|
||||||
dialogData.value.rowData.cmdStr = itemCmd ? itemCmd.substring(0, itemCmd.length - 1) : '';
|
form.tty = res.data.tty;
|
||||||
|
form.publishAllPorts = res.data.publishAllPorts;
|
||||||
let itemEntrypoint = '';
|
form.nanoCPUs = res.data.nanoCPUs;
|
||||||
if (dialogData.value.rowData?.entrypoint) {
|
form.cpuShares = res.data.cpuShares;
|
||||||
for (const item of dialogData.value.rowData.entrypoint) {
|
form.privileged = res.data.privileged;
|
||||||
itemEntrypoint += `'${item}' `;
|
form.autoRemove = res.data.autoRemove;
|
||||||
|
form.restartPolicy = res.data.restartPolicy;
|
||||||
|
form.memory = Number(res.data.memory.toFixed(2));
|
||||||
|
form.cmd = res.data.cmd || [];
|
||||||
|
let itemCmd = '';
|
||||||
|
for (const item of form.cmd) {
|
||||||
|
itemCmd += `'${item}' `;
|
||||||
}
|
}
|
||||||
}
|
form.cmdStr = itemCmd ? itemCmd.substring(0, itemCmd.length - 1) : '';
|
||||||
|
|
||||||
dialogData.value.rowData.entrypointStr = itemEntrypoint
|
let itemEntrypoint = '';
|
||||||
? itemEntrypoint.substring(0, itemEntrypoint.length - 1)
|
if (res.data.entrypoint) {
|
||||||
: '';
|
for (const item of res.data.entrypoint) {
|
||||||
dialogData.value.rowData.labels = dialogData.value.rowData.labels || [];
|
itemEntrypoint += `'${item}' `;
|
||||||
dialogData.value.rowData.env = dialogData.value.rowData.env || [];
|
}
|
||||||
dialogData.value.rowData.labelsStr = dialogData.value.rowData.labels.join('\n');
|
|
||||||
dialogData.value.rowData.envStr = dialogData.value.rowData.env.join('\n');
|
|
||||||
dialogData.value.rowData.exposedPorts = dialogData.value.rowData.exposedPorts || [];
|
|
||||||
for (const item of dialogData.value.rowData.exposedPorts) {
|
|
||||||
if (item.hostIP) {
|
|
||||||
item.host = item.hostIP + ':' + item.hostPort;
|
|
||||||
} else {
|
|
||||||
item.host = item.hostPort;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form.entrypointStr = itemEntrypoint ? itemEntrypoint.substring(0, itemEntrypoint.length - 1) : '';
|
||||||
|
form.labels = res.data.labels || [];
|
||||||
|
form.env = res.data.env || [];
|
||||||
|
form.labelsStr = res.data.labels.join('\n');
|
||||||
|
form.envStr = res.data.env.join('\n');
|
||||||
|
form.exposedPorts = res.data.exposedPorts || [];
|
||||||
|
for (const item of res.data.exposedPorts) {
|
||||||
|
if (item.hostIP) {
|
||||||
|
item.host = item.hostIP + ':' + item.hostPort;
|
||||||
|
} else {
|
||||||
|
item.host = item.hostPort;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
form.volumes = res.data.volumes || [];
|
||||||
}
|
}
|
||||||
dialogData.value.rowData.volumes = dialogData.value.rowData.volumes || [];
|
|
||||||
}
|
}
|
||||||
loadLimit();
|
loadLimit();
|
||||||
loadImageOptions();
|
loadImageOptions();
|
||||||
loadVolumeOptions();
|
loadVolumeOptions();
|
||||||
loadNetworkOptions();
|
loadNetworkOptions();
|
||||||
drawerVisible.value = true;
|
|
||||||
};
|
};
|
||||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
|
||||||
|
|
||||||
const images = ref();
|
const images = ref();
|
||||||
const volumes = ref();
|
const volumes = ref();
|
||||||
|
|
@ -333,11 +397,6 @@ const limits = ref<Container.ResourceLimit>({
|
||||||
memory: null as number,
|
memory: null as number,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
emit('search');
|
|
||||||
drawerVisible.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [Rules.requiredInput, Rules.containerName],
|
name: [Rules.requiredInput, Rules.containerName],
|
||||||
image: [Rules.imageName],
|
image: [Rules.imageName],
|
||||||
|
|
@ -349,6 +408,10 @@ const rules = reactive({
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
const formRef = ref<FormInstance>();
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const goBack = () => {
|
||||||
|
router.push({ name: 'Container' });
|
||||||
|
};
|
||||||
|
|
||||||
const handlePortsAdd = () => {
|
const handlePortsAdd = () => {
|
||||||
let item = {
|
let item = {
|
||||||
host: '',
|
host: '',
|
||||||
|
|
@ -357,10 +420,10 @@ const handlePortsAdd = () => {
|
||||||
hostPort: '',
|
hostPort: '',
|
||||||
protocol: 'tcp',
|
protocol: 'tcp',
|
||||||
};
|
};
|
||||||
dialogData.value.rowData!.exposedPorts.push(item);
|
form.exposedPorts.push(item);
|
||||||
};
|
};
|
||||||
const handlePortsDelete = (index: number) => {
|
const handlePortsDelete = (index: number) => {
|
||||||
dialogData.value.rowData!.exposedPorts.splice(index, 1);
|
form.exposedPorts.splice(index, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const goRouter = async () => {
|
const goRouter = async () => {
|
||||||
|
|
@ -374,10 +437,10 @@ const handleVolumesAdd = () => {
|
||||||
containerDir: '',
|
containerDir: '',
|
||||||
mode: 'rw',
|
mode: 'rw',
|
||||||
};
|
};
|
||||||
dialogData.value.rowData!.volumes.push(item);
|
form.volumes.push(item);
|
||||||
};
|
};
|
||||||
const handleVolumesDelete = (index: number) => {
|
const handleVolumesDelete = (index: number) => {
|
||||||
dialogData.value.rowData!.volumes.splice(index, 1);
|
form.volumes.splice(index, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadLimit = async () => {
|
const loadLimit = async () => {
|
||||||
|
|
@ -399,8 +462,8 @@ const loadNetworkOptions = async () => {
|
||||||
networks.value = res.data;
|
networks.value = res.data;
|
||||||
};
|
};
|
||||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
if (dialogData.value.rowData!.volumes.length !== 0) {
|
if (form.volumes.length !== 0) {
|
||||||
for (const item of dialogData.value.rowData!.volumes) {
|
for (const item of form.volumes) {
|
||||||
if (!item.containerDir || !item.sourceDir) {
|
if (!item.containerDir || !item.sourceDir) {
|
||||||
MsgError(i18n.global.t('container.volumeHelper'));
|
MsgError(i18n.global.t('container.volumeHelper'));
|
||||||
return;
|
return;
|
||||||
|
|
@ -410,62 +473,60 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
if (dialogData.value.rowData?.envStr) {
|
if (form.envStr) {
|
||||||
dialogData.value.rowData.env = dialogData.value.rowData!.envStr.split('\n');
|
form.env = form.envStr.split('\n');
|
||||||
}
|
}
|
||||||
if (dialogData.value.rowData?.labelsStr) {
|
if (form.labelsStr) {
|
||||||
dialogData.value.rowData!.labels = dialogData.value.rowData!.labelsStr.split('\n');
|
form.labels = form.labelsStr.split('\n');
|
||||||
}
|
}
|
||||||
dialogData.value.rowData!.cmd = [];
|
form.cmd = [];
|
||||||
if (dialogData.value.rowData?.cmdStr) {
|
if (form.cmdStr) {
|
||||||
if (dialogData.value.rowData?.cmdStr.indexOf(`'`) !== -1) {
|
if (form.cmdStr.indexOf(`'`) !== -1) {
|
||||||
let itemCmd = dialogData.value.rowData!.cmdStr.split(`'`);
|
let itemCmd = form.cmdStr.split(`'`);
|
||||||
for (const cmd of itemCmd) {
|
for (const cmd of itemCmd) {
|
||||||
if (cmd && cmd !== ' ') {
|
if (cmd && cmd !== ' ') {
|
||||||
dialogData.value.rowData!.cmd.push(cmd);
|
form.cmd.push(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let itemCmd = dialogData.value.rowData!.cmdStr.split(` `);
|
let itemCmd = form.cmdStr.split(` `);
|
||||||
for (const cmd of itemCmd) {
|
for (const cmd of itemCmd) {
|
||||||
dialogData.value.rowData!.cmd.push(cmd);
|
form.cmd.push(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialogData.value.rowData!.entrypoint = [];
|
form.entrypoint = [];
|
||||||
if (dialogData.value.rowData?.entrypointStr) {
|
if (form.entrypointStr) {
|
||||||
if (dialogData.value.rowData?.entrypointStr.indexOf(`'`) !== -1) {
|
if (form.entrypointStr.indexOf(`'`) !== -1) {
|
||||||
let itemEntrypoint = dialogData.value.rowData!.entrypointStr.split(`'`);
|
let itemEntrypoint = form.entrypointStr.split(`'`);
|
||||||
for (const entry of itemEntrypoint) {
|
for (const entry of itemEntrypoint) {
|
||||||
if (entry && entry !== ' ') {
|
if (entry && entry !== ' ') {
|
||||||
dialogData.value.rowData!.entrypoint.push(entry);
|
form.entrypoint.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let itemEntrypoint = dialogData.value.rowData!.entrypointStr.split(` `);
|
let itemEntrypoint = form.entrypointStr.split(` `);
|
||||||
for (const entry of itemEntrypoint) {
|
for (const entry of itemEntrypoint) {
|
||||||
dialogData.value.rowData!.entrypoint.push(entry);
|
form.entrypoint.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (dialogData.value.rowData!.publishAllPorts) {
|
if (form.publishAllPorts) {
|
||||||
dialogData.value.rowData!.exposedPorts = [];
|
form.exposedPorts = [];
|
||||||
} else {
|
} else {
|
||||||
if (!checkPortValid()) {
|
if (!checkPortValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialogData.value.rowData!.memory = Number(dialogData.value.rowData!.memory);
|
form.memory = Number(form.memory);
|
||||||
dialogData.value.rowData!.nanoCPUs = Number(dialogData.value.rowData!.nanoCPUs);
|
form.nanoCPUs = Number(form.nanoCPUs);
|
||||||
|
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
if (dialogData.value.title === 'create') {
|
if (isCreate.value) {
|
||||||
await createContainer(dialogData.value.rowData!)
|
await createContainer(form)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
emit('search');
|
|
||||||
drawerVisible.value = false;
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
|
|
@ -480,12 +541,10 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await updateContainer(dialogData.value.rowData!)
|
await updateContainer(form)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
emit('search');
|
|
||||||
drawerVisible.value = false;
|
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
updateContainerID();
|
updateContainerID();
|
||||||
|
|
@ -504,24 +563,24 @@ const updateContainerID = async () => {
|
||||||
page: 1,
|
page: 1,
|
||||||
pageSize: 1,
|
pageSize: 1,
|
||||||
state: 'all',
|
state: 'all',
|
||||||
name: dialogData.value.rowData.name,
|
name: form.name,
|
||||||
filters: '',
|
filters: '',
|
||||||
orderBy: 'created_at',
|
orderBy: 'created_at',
|
||||||
order: 'null',
|
order: 'null',
|
||||||
};
|
};
|
||||||
await searchContainer(params).then((res) => {
|
await searchContainer(params).then((res) => {
|
||||||
if (res.data.items?.length === 1) {
|
if (res.data.items?.length === 1) {
|
||||||
dialogData.value.rowData.containerID = res.data.items[0].containerID;
|
form.containerID = res.data.items[0].containerID;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkPortValid = () => {
|
const checkPortValid = () => {
|
||||||
if (dialogData.value.rowData!.exposedPorts.length === 0) {
|
if (form.exposedPorts.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (const port of dialogData.value.rowData!.exposedPorts) {
|
for (const port of form.exposedPorts) {
|
||||||
if (port.host.indexOf(':') !== -1) {
|
if (port.host.indexOf(':') !== -1) {
|
||||||
port.hostIP = port.host.substring(0, port.host.lastIndexOf(':'));
|
port.hostIP = port.host.substring(0, port.host.lastIndexOf(':'));
|
||||||
if (checkIpV4V6(port.hostIP)) {
|
if (checkIpV4V6(port.hostIP)) {
|
||||||
|
|
@ -573,8 +632,14 @@ const isFromApp = (rowData: Container.ContainerHelper) => {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
defineExpose({
|
onMounted(() => {
|
||||||
acceptParams,
|
if (router.currentRoute.value.query.containerID) {
|
||||||
|
isCreate.value = false;
|
||||||
|
form.containerID = String(router.currentRoute.value.query.containerID);
|
||||||
|
} else {
|
||||||
|
isCreate.value = true;
|
||||||
|
}
|
||||||
|
search();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue