mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-11-01 11:46:34 +08:00
fix: 增加 docker 仓库授信删除判断
This commit is contained in:
parent
bdecd777a7
commit
326941ad48
13 changed files with 140 additions and 31 deletions
|
|
@ -59,7 +59,7 @@ func (b *BaseApi) CreateRepo(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
||||||
var req dto.BatchDeleteReq
|
var req dto.ImageRepoDelete
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
|
|
@ -69,7 +69,7 @@ func (b *BaseApi) DeleteRepo(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := imageRepoService.BatchDelete(req.Ids); err != nil {
|
if err := imageRepoService.BatchDelete(req); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,3 +38,8 @@ type ImageRepoOption struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
DownloadUrl string `json:"downloadUrl"`
|
DownloadUrl string `json:"downloadUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ImageRepoDelete struct {
|
||||||
|
DeleteInsecure bool `json:"deleteInsecure"`
|
||||||
|
Ids []uint `json:"ids" validate:"required"`
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ type IImageRepoService interface {
|
||||||
List() ([]dto.ImageRepoOption, error)
|
List() ([]dto.ImageRepoOption, error)
|
||||||
Create(req dto.ImageRepoCreate) error
|
Create(req dto.ImageRepoCreate) error
|
||||||
Update(req dto.ImageRepoUpdate) error
|
Update(req dto.ImageRepoUpdate) error
|
||||||
BatchDelete(ids []uint) error
|
BatchDelete(req dto.ImageRepoDelete) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIImageRepoService() IImageRepoService {
|
func NewIImageRepoService() IImageRepoService {
|
||||||
|
|
@ -70,6 +70,13 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||||
if err := copier.Copy(&imageRepo, &req); err != nil {
|
if err := copier.Copy(&imageRepo, &req); err != nil {
|
||||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command("systemctl", "restart", "docker")
|
||||||
|
stdout, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(string(stdout))
|
||||||
|
}
|
||||||
|
|
||||||
imageRepo.Status = constant.StatusSuccess
|
imageRepo.Status = constant.StatusSuccess
|
||||||
if err := u.checkConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
if err := u.checkConn(req.DownloadUrl, req.Username, req.Password); err != nil {
|
||||||
imageRepo.Status = constant.StatusFailed
|
imageRepo.Status = constant.StatusFailed
|
||||||
|
|
@ -79,21 +86,22 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command("systemctl", "restart", "docker")
|
|
||||||
stdout, err := cmd.CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return errors.New(string(stdout))
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ImageRepoService) BatchDelete(ids []uint) error {
|
func (u *ImageRepoService) BatchDelete(req dto.ImageRepoDelete) error {
|
||||||
for _, id := range ids {
|
for _, id := range req.Ids {
|
||||||
if id == 1 {
|
if id == 1 {
|
||||||
return errors.New("The default value cannot be edit !")
|
return errors.New("The default value cannot be edit !")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
repos, err := imageRepoRepo.List(commonRepo.WithIdsIn(ids))
|
if !req.DeleteInsecure {
|
||||||
|
if err := imageRepoRepo.Delete(commonRepo.WithIdsIn(req.Ids)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
repos, err := imageRepoRepo.List(commonRepo.WithIdsIn(req.Ids))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +114,7 @@ func (u *ImageRepoService) BatchDelete(ids []uint) error {
|
||||||
_, _ = cmd.CombinedOutput()
|
_, _ = cmd.CombinedOutput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := imageRepoRepo.Delete(commonRepo.WithIdsIn(ids)); err != nil {
|
if err := imageRepoRepo.Delete(commonRepo.WithIdsIn(req.Ids)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cmd := exec.Command("systemctl", "restart", "docker")
|
cmd := exec.Command("systemctl", "restart", "docker")
|
||||||
|
|
@ -129,9 +137,14 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
|
||||||
if repo.DownloadUrl != req.DownloadUrl {
|
if repo.DownloadUrl != req.DownloadUrl {
|
||||||
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
|
_ = u.handleRegistries(req.DownloadUrl, repo.DownloadUrl, "update")
|
||||||
if repo.Auth {
|
if repo.Auth {
|
||||||
cmd := exec.Command("docker", "logout", fmt.Sprintf("%s://%s", repo.Protocol, repo.DownloadUrl))
|
cmd := exec.Command("docker", "logout", repo.DownloadUrl)
|
||||||
_, _ = cmd.CombinedOutput()
|
_, _ = cmd.CombinedOutput()
|
||||||
}
|
}
|
||||||
|
cmd := exec.Command("systemctl", "restart", "docker")
|
||||||
|
stdout, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return errors.New(string(stdout))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upMap := make(map[string]interface{})
|
upMap := make(map[string]interface{})
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,10 @@ export namespace Container {
|
||||||
password: string;
|
password: string;
|
||||||
auth: boolean;
|
auth: boolean;
|
||||||
}
|
}
|
||||||
|
export interface RepoDelete {
|
||||||
|
ids: Array<number>;
|
||||||
|
deleteInsecure: boolean;
|
||||||
|
}
|
||||||
export interface RepoInfo {
|
export interface RepoInfo {
|
||||||
id: number;
|
id: number;
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ export const createImageRepo = (params: Container.RepoCreate) => {
|
||||||
export const updateImageRepo = (params: Container.RepoUpdate) => {
|
export const updateImageRepo = (params: Container.RepoUpdate) => {
|
||||||
return http.post(`/containers/repo/update`, params);
|
return http.post(`/containers/repo/update`, params);
|
||||||
};
|
};
|
||||||
export const deleteImageRepo = (params: { ids: number[] }) => {
|
export const deleteImageRepo = (params: Container.RepoDelete) => {
|
||||||
return http.post(`/containers/repo/del`, params);
|
return http.post(`/containers/repo/del`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,9 @@ export default {
|
||||||
repo: 'Repo',
|
repo: 'Repo',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
protocol: 'protocol',
|
protocol: 'protocol',
|
||||||
|
httpRepo: 'The http repository needs to restart the docker service to add credit',
|
||||||
|
delInsecure: 'Deletion of credit',
|
||||||
|
delInsecureHelper: 'docker service needs to be restarted to delete the credit. Do you want to delete it?',
|
||||||
downloadUrl: 'Download URL',
|
downloadUrl: 'Download URL',
|
||||||
imageRepo: 'Image repo',
|
imageRepo: 'Image repo',
|
||||||
repoHelper: 'Does it include a mirror repository/organization/project?',
|
repoHelper: 'Does it include a mirror repository/organization/project?',
|
||||||
|
|
|
||||||
|
|
@ -415,6 +415,9 @@ export default {
|
||||||
imageDelete: '删除镜像',
|
imageDelete: '删除镜像',
|
||||||
repoName: '仓库名',
|
repoName: '仓库名',
|
||||||
imageName: '镜像名',
|
imageName: '镜像名',
|
||||||
|
httpRepo: 'http 仓库添加授信需要重启 docker 服务',
|
||||||
|
delInsecure: '删除授信',
|
||||||
|
delInsecureHelper: '删除授信需要重启 docker 服务,是否删除?',
|
||||||
pull: '拉取',
|
pull: '拉取',
|
||||||
path: '路径',
|
path: '路径',
|
||||||
importImage: '导入镜像',
|
importImage: '导入镜像',
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ const search = async () => {
|
||||||
};
|
};
|
||||||
const loadRepos = async () => {
|
const loadRepos = async () => {
|
||||||
const res = await listImageRepo();
|
const res = await listImageRepo();
|
||||||
repos.value = res.data;
|
repos.value = res.data || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
const onOpenPull = () => {
|
const onOpenPull = () => {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
prop="repoID"
|
prop="repoID"
|
||||||
>
|
>
|
||||||
<el-select style="width: 100%" filterable v-model="form.repoID">
|
<el-select style="width: 100%" filterable v-model="form.repoID">
|
||||||
<el-option v-for="item in dialogData.repos" :key="item.id" :value="item.id" :label="item.name" />
|
<el-option v-for="item in repos" :key="item.id" :value="item.id" :label="item.name" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('container.imageName')" :rules="Rules.requiredInput" prop="imageName">
|
<el-form-item :label="$t('container.imageName')" :rules="Rules.requiredInput" prop="imageName">
|
||||||
|
|
@ -75,7 +75,7 @@ import { LoadFile } from '@/api/modules/files';
|
||||||
const pullVisiable = ref(false);
|
const pullVisiable = ref(false);
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
fromRepo: true,
|
fromRepo: true,
|
||||||
repoID: 1,
|
repoID: null as number,
|
||||||
imageName: '',
|
imageName: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -89,16 +89,13 @@ let timer: NodeJS.Timer | null = null;
|
||||||
interface DialogProps {
|
interface DialogProps {
|
||||||
repos: Array<Container.RepoOptions>;
|
repos: Array<Container.RepoOptions>;
|
||||||
}
|
}
|
||||||
const dialogData = ref<DialogProps>({
|
const repos = ref();
|
||||||
repos: [] as Array<Container.RepoOptions>,
|
|
||||||
});
|
|
||||||
|
|
||||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||||
pullVisiable.value = true;
|
pullVisiable.value = true;
|
||||||
form.fromRepo = true;
|
form.fromRepo = true;
|
||||||
form.repoID = 1;
|
|
||||||
form.imageName = '';
|
form.imageName = '';
|
||||||
dialogData.value.repos = params.repos;
|
repos.value = params.repos;
|
||||||
buttonDisabled.value = false;
|
buttonDisabled.value = false;
|
||||||
logInfo.value = '';
|
logInfo.value = '';
|
||||||
};
|
};
|
||||||
|
|
@ -138,7 +135,7 @@ const onCloseLog = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadDetailInfo(id: number) {
|
function loadDetailInfo(id: number) {
|
||||||
for (const item of dialogData.value.repos) {
|
for (const item of repos.value) {
|
||||||
if (item.id === id) {
|
if (item.id === id) {
|
||||||
return item.downloadUrl;
|
return item.downloadUrl;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
66
frontend/src/views/container/repo/delete/index.vue
Normal file
66
frontend/src/views/container/repo/delete/index.vue
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog v-model="repoVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ $t('commons.button.delete') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form v-loading="loading" label-width="20px">
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="isDelete">{{ $t('container.delInsecure') }}</el-checkbox>
|
||||||
|
<span class="input-help">{{ $t('container.delInsecureHelper') }}</span>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button :disabled="loading" @click="repoVisiable = false">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="loading" type="primary" @click="onSubmit()">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
|
import { deleteImageRepo } from '@/api/modules/container';
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const isDelete = ref(false);
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
ids?: Array<number>;
|
||||||
|
}
|
||||||
|
const repoVisiable = ref(false);
|
||||||
|
const ids = ref();
|
||||||
|
|
||||||
|
const acceptParams = (params: DialogProps): void => {
|
||||||
|
ids.value = params.ids;
|
||||||
|
repoVisiable.value = true;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
const onSubmit = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await deleteImageRepo({ ids: ids.value, deleteInsecure: isDelete.value })
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
repoVisiable.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -47,18 +47,19 @@
|
||||||
</ComplexTable>
|
</ComplexTable>
|
||||||
</el-card>
|
</el-card>
|
||||||
<OperatorDialog @search="search" ref="dialogRef" />
|
<OperatorDialog @search="search" ref="dialogRef" />
|
||||||
|
<DeleteDialog @search="search" ref="dialogDeleteRef" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import ComplexTable from '@/components/complex-table/index.vue';
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
import OperatorDialog from '@/views/container/repo/operator/index.vue';
|
import OperatorDialog from '@/views/container/repo/operator/index.vue';
|
||||||
|
import DeleteDialog from '@/views/container/repo/delete/index.vue';
|
||||||
import Submenu from '@/views/container/index.vue';
|
import Submenu from '@/views/container/index.vue';
|
||||||
import { reactive, onMounted, ref } from 'vue';
|
import { reactive, onMounted, ref } from 'vue';
|
||||||
import { dateFromat } from '@/utils/util';
|
import { dateFromat } from '@/utils/util';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import { deleteImageRepo, loadDockerStatus, searchImageRepo } from '@/api/modules/container';
|
import { loadDockerStatus, searchImageRepo } from '@/api/modules/container';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
|
|
||||||
|
|
@ -112,6 +113,7 @@ const onOpenDialog = async (
|
||||||
dialogRef.value!.acceptParams(params);
|
dialogRef.value!.acceptParams(params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const dialogDeleteRef = ref();
|
||||||
const onBatchDelete = async (row: Container.RepoInfo | null) => {
|
const onBatchDelete = async (row: Container.RepoInfo | null) => {
|
||||||
let ids: Array<number> = [];
|
let ids: Array<number> = [];
|
||||||
if (row) {
|
if (row) {
|
||||||
|
|
@ -121,8 +123,7 @@ const onBatchDelete = async (row: Container.RepoInfo | null) => {
|
||||||
ids.push(item.id);
|
ids.push(item.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await useDeleteData(deleteImageRepo, { ids: ids }, 'commons.msg.delete');
|
dialogDeleteRef.value!.acceptParams({ ids: ids });
|
||||||
search();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,11 @@
|
||||||
</template>
|
</template>
|
||||||
<el-form ref="formRef" v-loading="loading" :model="dialogData.rowData" :rules="rules" label-width="120px">
|
<el-form ref="formRef" v-loading="loading" :model="dialogData.rowData" :rules="rules" label-width="120px">
|
||||||
<el-form-item :label="$t('container.name')" prop="name">
|
<el-form-item :label="$t('container.name')" prop="name">
|
||||||
<el-input :disabled="dialogData.title === 'edit'" v-model="dialogData.rowData!.name"></el-input>
|
<el-input
|
||||||
|
clearable
|
||||||
|
:disabled="dialogData.title === 'edit'"
|
||||||
|
v-model="dialogData.rowData!.name"
|
||||||
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('container.auth')" prop="auth">
|
<el-form-item :label="$t('container.auth')" prop="auth">
|
||||||
<el-radio-group v-model="dialogData.rowData!.auth">
|
<el-radio-group v-model="dialogData.rowData!.auth">
|
||||||
|
|
@ -16,19 +20,29 @@
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="dialogData.rowData!.auth" :label="$t('commons.login.username')" prop="username">
|
<el-form-item v-if="dialogData.rowData!.auth" :label="$t('commons.login.username')" prop="username">
|
||||||
<el-input v-model="dialogData.rowData!.username"></el-input>
|
<el-input clearable v-model="dialogData.rowData!.username"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="dialogData.rowData!.auth" :label="$t('commons.login.password')" prop="password">
|
<el-form-item v-if="dialogData.rowData!.auth" :label="$t('commons.login.password')" prop="password">
|
||||||
<el-input type="password" v-model="dialogData.rowData!.password"></el-input>
|
<el-input clearable type="password" show-password v-model="dialogData.rowData!.password"></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('container.downloadUrl')" prop="downloadUrl">
|
<el-form-item :label="$t('container.downloadUrl')" prop="downloadUrl">
|
||||||
<el-input v-model="dialogData.rowData!.downloadUrl" :placeholder="'172.16.10.10:8081'"></el-input>
|
<el-input
|
||||||
|
clearable
|
||||||
|
v-model="dialogData.rowData!.downloadUrl"
|
||||||
|
:placeholder="'172.16.10.10:8081'"
|
||||||
|
></el-input>
|
||||||
|
<span v-if="dialogData.rowData!.downloadUrl" class="input-help">
|
||||||
|
docker pull {{ dialogData.rowData!.downloadUrl }}/nginx
|
||||||
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('container.protocol')" prop="protocol">
|
<el-form-item :label="$t('container.protocol')" prop="protocol">
|
||||||
<el-radio-group v-model="dialogData.rowData!.protocol">
|
<el-radio-group v-model="dialogData.rowData!.protocol">
|
||||||
<el-radio label="http">http</el-radio>
|
<el-radio label="http">http</el-radio>
|
||||||
<el-radio label="https">https</el-radio>
|
<el-radio label="https">https</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
|
<span v-if="dialogData.rowData!.protocol === 'http'" class="input-help">
|
||||||
|
{{ $t('container.httpRepo') }}
|
||||||
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|
|
||||||
|
|
@ -282,6 +282,9 @@ const loadHost = async () => {
|
||||||
const res = await getHostTree({});
|
const res = await getHostTree({});
|
||||||
hostTree.value = res.data;
|
hostTree.value = res.data;
|
||||||
for (const item of hostTree.value) {
|
for (const item of hostTree.value) {
|
||||||
|
if (!item.children) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (const host of item.children) {
|
for (const host of item.children) {
|
||||||
if (host.label.indexOf('127.0.0.1')) {
|
if (host.label.indexOf('127.0.0.1')) {
|
||||||
localHostID.value = host.id;
|
localHostID.value = host.id;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue