11<script setup lang="ts">
2- import { NButton , NDataTable , NPopconfirm , NTag } from ' naive-ui'
2+ import { NButton , NDataTable , NPopconfirm , NPopover , NSpin , NTag } from ' naive-ui'
33import { useGettext } from ' vue3-gettext'
44
55import firewall from ' @/api/panel/firewall'
@@ -8,6 +8,23 @@ import CreateModal from '@/views/firewall/CreateModal.vue'
88const { $gettext } = useGettext ()
99const createModalShow = ref (false )
1010
11+ // 端口进程信息缓存
12+ const portUsageCache = ref <Record <string , any >>({})
13+ const portUsageLoading = ref <Record <string , boolean >>({})
14+
15+ const fetchPortUsage = async (port : number , protocol : string ) => {
16+ const key = ` ${protocol }:${port } `
17+ if (portUsageCache .value [key ]) return
18+ portUsageLoading .value [key ] = true
19+ try {
20+ const proto = protocol === ' tcp/udp' ? ' tcp' : protocol
21+ const data = await firewall .portUsage (port , proto )
22+ portUsageCache .value [key ] = data || []
23+ } finally {
24+ portUsageLoading .value [key ] = false
25+ }
26+ }
27+
1128const columns: any = [
1229 { type: ' selection' , fixed: ' left' },
1330 {
@@ -62,17 +79,67 @@ const columns: any = [
6279 key: ' in_use' ,
6380 width: 150 ,
6481 render(row : any ): any {
82+ if (! row .in_use ) {
83+ return h (NTag , { type: ' default' }, { default : () => $gettext (' Not Used' ) })
84+ }
85+
86+ const key = ` ${row .protocol }:${row .port_start } `
6587 return h (
66- NTag ,
88+ NPopover ,
6789 {
68- type: row .in_use ? ' success' : ' default'
90+ trigger: ' click' ,
91+ placement: ' bottom' ,
92+ onUpdateShow : (show : boolean ) => {
93+ if (show ) fetchPortUsage (row .port_start , row .protocol )
94+ }
6995 },
7096 {
97+ trigger : () =>
98+ h (
99+ NTag ,
100+ {
101+ type: ' success' ,
102+ style: ' cursor: pointer;'
103+ },
104+ { default : () => $gettext (' In Use' ) }
105+ ),
71106 default : () => {
72- if (row .in_use ) {
73- return $gettext (' In Use' )
107+ if (portUsageLoading .value [key ]) {
108+ return h (NSpin , { size: ' small' , style: ' padding: 12px;' })
109+ }
110+ const processes = portUsageCache .value [key ]
111+ if (! processes || processes .length === 0 ) {
112+ return h (' div' , { style: ' padding: 4px; color: var(--n-text-color);' }, $gettext (' No process information' ))
74113 }
75- return $gettext (' Not Used' )
114+ return h (
115+ ' div' ,
116+ { style: ' max-height: 300px; overflow-y: auto;' },
117+ processes .map ((p : any , i : number ) => {
118+ return h (
119+ ' div' ,
120+ {
121+ style:
122+ i > 0
123+ ? ' padding-top: 8px; margin-top: 8px; border-top: 1px solid var(--n-border-color);'
124+ : ' '
125+ },
126+ [
127+ h (' div' , { style: ' font-size: 13px;' }, [
128+ h (' span' , { style: ' font-weight: bold;' }, ` ${p .name } ` ),
129+ h (' span' , { style: ' margin-left: 8px; opacity: 0.6;' }, ` PID: ${p .pid } ` )
130+ ]),
131+ h (
132+ ' div' ,
133+ {
134+ style:
135+ ' font-size: 12px; opacity: 0.8; margin-top: 4px; word-break: break-all; font-family: monospace;'
136+ },
137+ p .command
138+ )
139+ ]
140+ )
141+ })
142+ )
76143 }
77144 }
78145 )
0 commit comments