Skip to content

Commit cb74d8d

Browse files
authored
feat: Add network details drawer component (#10836)
1 parent eaa620b commit cb74d8d

File tree

2 files changed

+191
-10
lines changed

2 files changed

+191
-10
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
<template>
2+
<DrawerPro v-model="visible" :header="$t('commons.button.view')" size="large" @close="handleClose">
3+
<el-tabs v-model="activeTab" type="border-card">
4+
<el-tab-pane :label="$t('container.network')" name="overview">
5+
<el-descriptions :column="1" border :title="$t('home.baseInfo')">
6+
<el-descriptions-item :label="$t('commons.table.name')">
7+
<el-text>{{ networkData?.Name || '-' }}</el-text>
8+
<CopyButton :content="networkData?.Name || ''" />
9+
</el-descriptions-item>
10+
<el-descriptions-item label="ID">
11+
<el-text class="text-xs">{{ networkData?.Id?.substring(0, 12) }}</el-text>
12+
<CopyButton :content="networkData?.Id || ''" />
13+
</el-descriptions-item>
14+
<el-descriptions-item :label="$t('container.driver')">
15+
{{ networkData?.Driver || '-' }}
16+
</el-descriptions-item>
17+
<el-descriptions-item :label="$t('commons.table.createdAt')">
18+
{{ formatDate(networkData?.Created) }}
19+
</el-descriptions-item>
20+
<el-descriptions-item label="IPv4">
21+
<el-tag :type="networkData?.EnableIPv4 ? 'success' : 'info'">
22+
{{ networkData?.EnableIPv4 ? $t('commons.status.enable') : $t('commons.status.disable') }}
23+
</el-tag>
24+
</el-descriptions-item>
25+
<el-descriptions-item label="IPv6">
26+
<el-tag :type="networkData?.EnableIPv6 ? 'success' : 'info'">
27+
{{ networkData?.EnableIPv6 ? $t('commons.status.enable') : $t('commons.status.disable') }}
28+
</el-tag>
29+
</el-descriptions-item>
30+
</el-descriptions>
31+
32+
<el-descriptions class="mt-4" :column="1" border title="IPAM">
33+
<el-descriptions-item :label="$t('container.driver')">
34+
{{ networkData?.IPAM?.Driver || '-' }}
35+
</el-descriptions-item>
36+
<el-descriptions-item
37+
v-for="(config, index) in networkData?.IPAM?.Config"
38+
:key="index"
39+
:label="$t('container.subnet') + (index > 0 ? ' ' + (index + 1) : '')"
40+
>
41+
<div v-if="config">
42+
<div v-if="config.Subnet">
43+
<el-text class="mr-2">{{ $t('container.subnet') }}:</el-text>
44+
<el-tag>{{ config.Subnet }}</el-tag>
45+
</div>
46+
<div v-if="config.Gateway" class="mt-1">
47+
<el-text class="mr-2">{{ $t('container.gateway') }}:</el-text>
48+
<el-tag>{{ config.Gateway }}</el-tag>
49+
</div>
50+
<div v-if="config.IPRange" class="mt-1">
51+
<el-text class="mr-2">{{ $t('container.scope') }}:</el-text>
52+
<el-tag>{{ config.IPRange }}</el-tag>
53+
</div>
54+
</div>
55+
<span v-else>-</span>
56+
</el-descriptions-item>
57+
</el-descriptions>
58+
59+
<div class="mt-4">
60+
<span class="block text-base font-medium text-el-color-primary">{{ $t('menu.container') }}</span>
61+
<el-table :data="containerList" border class="mt-2">
62+
<el-table-column :label="$t('commons.table.name')" min-width="120">
63+
<template #default="{ row }">
64+
<el-text>{{ row.name }}</el-text>
65+
<CopyButton :content="row.name" />
66+
</template>
67+
</el-table-column>
68+
<el-table-column label="IPv4" min-width="100">
69+
<template #default="{ row }">
70+
{{ row.ipv4 || '-' }}
71+
</template>
72+
</el-table-column>
73+
<el-table-column label="IPv6" min-width="100">
74+
<template #default="{ row }">
75+
{{ row.ipv6 || '-' }}
76+
</template>
77+
</el-table-column>
78+
<el-table-column label="MAC" min-width="120">
79+
<template #default="{ row }">
80+
{{ row.mac || '-' }}
81+
</template>
82+
</el-table-column>
83+
<el-table-column label="Endpoint ID" width="120">
84+
<template #default="{ row }">
85+
<el-text class="text-xs">{{ row.endpointId?.substring(0, 12) || '-' }}</el-text>
86+
</template>
87+
</el-table-column>
88+
</el-table>
89+
</div>
90+
</el-tab-pane>
91+
92+
<el-tab-pane :label="$t('commons.button.view')" name="raw">
93+
<CodemirrorPro v-model="rawJson" :height-diff="240" :disabled="true" mode="json" />
94+
</el-tab-pane>
95+
</el-tabs>
96+
97+
<template #footer>
98+
<span class="dialog-footer">
99+
<el-button @click="visible = false">{{ $t('commons.button.cancel') }}</el-button>
100+
</span>
101+
</template>
102+
</DrawerPro>
103+
</template>
104+
105+
<script lang="ts" setup>
106+
import { ref, computed } from 'vue';
107+
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
108+
109+
const visible = ref(false);
110+
const activeTab = ref('overview');
111+
const networkData = ref<any>(null);
112+
const rawJson = ref('');
113+
114+
interface DialogProps {
115+
data: any;
116+
}
117+
118+
const acceptParams = (props: DialogProps): void => {
119+
visible.value = true;
120+
activeTab.value = 'overview';
121+
122+
try {
123+
if (typeof props.data === 'string') {
124+
networkData.value = JSON.parse(props.data);
125+
} else {
126+
networkData.value = props.data;
127+
}
128+
rawJson.value = JSON.stringify(networkData.value, null, 2);
129+
} catch (e) {
130+
console.error('Failed to parse network data:', e);
131+
}
132+
};
133+
134+
const handleClose = () => {
135+
visible.value = false;
136+
networkData.value = null;
137+
rawJson.value = '';
138+
};
139+
140+
const formatDate = (dateStr: string): string => {
141+
if (!dateStr || dateStr === '0001-01-01T00:00:00Z') {
142+
return '-';
143+
}
144+
try {
145+
const date = new Date(dateStr);
146+
const y = date.getFullYear();
147+
const m = String(date.getMonth() + 1).padStart(2, '0');
148+
const d = String(date.getDate()).padStart(2, '0');
149+
const h = String(date.getHours()).padStart(2, '0');
150+
const minute = String(date.getMinutes()).padStart(2, '0');
151+
const second = String(date.getSeconds()).padStart(2, '0');
152+
return `${y}-${m}-${d} ${h}:${minute}:${second}`;
153+
} catch {
154+
return dateStr;
155+
}
156+
};
157+
158+
const containerList = computed(() => {
159+
if (!networkData.value?.Containers) {
160+
return [];
161+
}
162+
163+
return Object.entries(networkData.value.Containers).map(([id, container]: [string, any]) => ({
164+
id,
165+
name: container.Name || '-',
166+
ipv4: container.IPv4Address || '',
167+
ipv6: container.IPv6Address || '',
168+
mac: container.MacAddress || '',
169+
endpointId: container.EndpointID || '',
170+
}));
171+
});
172+
173+
defineExpose({
174+
acceptParams,
175+
});
176+
</script>
177+
178+
<style scoped lang="scss">
179+
:deep(.el-descriptions__label) {
180+
width: 180px;
181+
background-color: transparent !important;
182+
}
183+
.text-el-color-primary {
184+
color: var(--el-text-color-primary);
185+
}
186+
</style>

frontend/src/views/container/network/index.vue

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,15 @@
8686
</LayoutContent>
8787

8888
<OpDialog ref="opRef" @search="search" />
89-
<CodemirrorDrawer ref="myDetail" />
89+
<DetailDrawer ref="detailDrawerRef" />
9090
<CreateDialog @search="search" ref="dialogCreateRef" />
9191
<TaskLog ref="taskLogRef" width="70%" @close="search" />
9292
</div>
9393
</template>
9494

9595
<script lang="ts" setup>
9696
import CreateDialog from '@/views/container/network/create/index.vue';
97-
import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue';
97+
import DetailDrawer from '@/views/container/network/detail/index.vue';
9898
import { reactive, ref } from 'vue';
9999
import { dateFormat, newUUID } from '@/utils/util';
100100
import { deleteNetwork, searchNetwork, inspect, containerPrune } from '@/api/modules/container';
@@ -105,7 +105,7 @@ import { ElMessageBox } from 'element-plus';
105105
import DockerStatus from '@/views/container/docker-status/index.vue';
106106
107107
const loading = ref();
108-
const myDetail = ref();
108+
const detailDrawerRef = ref();
109109
const taskLogRef = ref();
110110
111111
const data = ref();
@@ -216,13 +216,8 @@ const batchDelete = async (row: Container.NetworkInfo | null) => {
216216
217217
const onInspect = async (id: string) => {
218218
const res = await inspect({ id: id, type: 'network' });
219-
let detailInfo = JSON.stringify(JSON.parse(res.data), null, 2);
220-
let param = {
221-
header: i18n.global.t('commons.button.view'),
222-
detailInfo: detailInfo,
223-
mode: 'json',
224-
};
225-
myDetail.value!.acceptParams(param);
219+
let networkData = JSON.parse(res.data);
220+
detailDrawerRef.value!.acceptParams({ data: networkData });
226221
};
227222
228223
function isSystem(val: string) {

0 commit comments

Comments
 (0)