1.背景

市面上已有很多顶级Docker 图形界面管理工具,出于学习容器开发目的,计划动手写个简单的界面管理工具

2. 技术选型

2.1 Wails

使用 Go 和 Web 技术编写桌面应用的项目,类似于ElectronJS 。

官方网站 https://wails.io/zh-Hans/docs/introduction/

2.2 Go

Docker本身采用Go编写,官网SDK如下

官方网站 https://docs.docker.com/engine/api/sdk/

2.3 Ant Design Vue

开箱即用的高质量 Vue 组件,使用方便

官方网站 https://www.antdv.com/docs/vue/introduce-cn

3. 当前功能

项目地址 https://github.com/LeoBest2/my-docker-gui

  • 容器查看、创建、启动、删除功能
  • 其他待研究
4. 效果图

在这里插入图片描述
在这里插入图片描述

5. 踩的坑备忘
ContainerStophandleRefresh

版本1

   <script setup>const handleStop = () => {spinning.value = true;selectedRows.value.forEach((row) => {ContainerStop(row.ID).then(() => { message.success(`容器${row.Names[0].substring(1)}停止成功!`); }).catch((e) => message.error(`容器${row.Names[0].substring(1)}停止失败: ${e}!`));});handleRefresh();
};</script>

版本2

   <script setup>const handleStop = () => {spinning.value = true;let promises = [];selectedRows.value.forEach((row) => {let p = ContainerStop(row.ID)p.then(() => { message.success(`容器${row.Names[0].substring(1)}停止成功!`); }).catch((e) => message.error(`容器${row.Names[0].substring(1)}停止失败: ${e}!`));promises.push(p);});Promise.all(promises).finally(() => { spinning.value = false; handleRefresh() });
};</script>

总代码

<template><a-spin :spinning="spinning"><div><div class="table-operations"><a-button @click="handleRefresh">刷新容器</a-button><a-popconfirm :title="'确定启动已选的 ' + selectedRows.length + ' 个容器?'" ok-text="确定" cancel-text="取消"@confirm="handleStart"><a-button>启动容器</a-button></a-popconfirm><a-popconfirm :title="'确定停止已选的 ' + selectedRows.length + ' 个容器?'" ok-text="确定" cancel-text="取消"@confirm="handleStop"><a-button>停止容器</a-button></a-popconfirm><a-popconfirm :title="'确定删除已选的 ' + selectedRows.length + ' 个容器?'" ok-text="确定" cancel-text="取消"@confirm="handleDelete"><a-button type="danger">删除容器</a-button></a-popconfirm></div><a-table :row-selection="{ onChange: (selectedRowKeys, _selectedRows) => selectedRows = _selectedRows }"row-key="ID" :columns="columns" :data-source="data" :scroll="{ x: 'max-content' }"><template #bodyCell="{ column, record }"><template v-if="column.dataIndex === 'Names'"><a-tag v-for="name in record.Names" color="geekblue">{{ name.substring(1) }}</a-tag></template><template v-else-if="column.dataIndex === 'State'"><a-tag :color="record.State == 'running' ? 'green' : 'volcano'">{{ record.State }}</a-tag></template><template v-else-if="column.dataIndex === 'Ports'"><a-tag v-for="port in record.Ports">{{ port.IP }}:{{ port.PublicPort }}->{{ port.PrivatePort }}/{{ port.Type }}</a-tag></template></template></a-table></div></a-spin>
</template>
<script setup>
import { ref } from 'vue';
import { ContainerList, ContainerStop, ContainerStart, ContainerDelete } from "../../wailsjs/go/main/App";
import { message } from "ant-design-vue";const spinning = ref(false);const columns = [{title: 'ID',dataIndex: 'ID',fixed: 'left',
}, {title: 'NAMES',dataIndex: 'Names',fixed: 'left',
}, {title: 'STATE',dataIndex: 'State',fixed: 'left',
}, {title: 'IMAGE',dataIndex: 'Image',
}, {title: 'COMMAND',dataIndex: 'Command',
}, {title: 'CREATED',dataIndex: 'Created',
}, {title: 'STATUS',dataIndex: 'Status',
}, {title: 'PORTS',dataIndex: 'Ports',
}];const data = ref([]);
const selectedRows = ref([]);const handleRefresh = () => {ContainerList().then((containers) => {data.value = containers;}).catch(e => message.error(e));
};const handleStart = () => {spinning.value = true;let promises = [];selectedRows.value.forEach((row) => {let p = ContainerStart(row.ID)p.then(() => message.success(`容器${row.Names[0].substring(1)}启动成功!`)).catch((e) => message.error(`容器${row.Names[0].substring(1)}启动失败: ${e}!`));promises.push(p);});Promise.all(promises).finally(() => { spinning.value = false; handleRefresh() });
};const handleStop = () => {spinning.value = true;let promises = [];selectedRows.value.forEach((row) => {let p = ContainerStop(row.ID)p.then(() => { message.success(`容器${row.Names[0].substring(1)}停止成功!`); }).catch((e) => message.error(`容器${row.Names[0].substring(1)}停止失败: ${e}!`));promises.push(p);});Promise.all(promises).finally(() => { spinning.value = false; handleRefresh() });
};const handleDelete = () => {spinning.value = true;let promises = [];selectedRows.value.forEach((row) => {let p = ContainerDelete(row.ID)p.then(() => { message.success(`容器${row.Names[0].substring(1)}停止成功!`); }).catch((e) => message.error(`容器${row.Names[0].substring(1)}停止失败: ${e}!`));promises.push(p);});Promise.all(promises).finally(() => { spinning.value = false; handleRefresh() });
};</script><style scoped>
.table-operations {margin-bottom: 16px;
}.table-operations>button {margin-right: 8px;
}
</style>