feat(network): 添加网络接口信息获取功能
- 新增 network-interface 依赖用于获取系统网络接口- 实现 Rust 端 get_network_interfaces 命令- 在 Svelte 前端调用 Tauri 命令并展示网络接口信息 - 添加接口信息表格展示及错误处理 - 更新布局结构移除多余注释和空行
This commit is contained in:
8
src-tauri/.idea/.gitignore
generated
vendored
Normal file
8
src-tauri/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
8
src-tauri/.idea/modules.xml
generated
Normal file
8
src-tauri/.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/src-tauri.iml" filepath="$PROJECT_DIR$/.idea/src-tauri.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
11
src-tauri/.idea/src-tauri.iml
generated
Normal file
11
src-tauri/.idea/src-tauri.iml
generated
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
src-tauri/.idea/vcs.xml
generated
Normal file
6
src-tauri/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
13
src-tauri/Cargo.lock
generated
13
src-tauri/Cargo.lock
generated
@@ -80,6 +80,7 @@ name = "app"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
|
"network-interface",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
@@ -1904,6 +1905,18 @@ dependencies = [
|
|||||||
"jni-sys",
|
"jni-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "network-interface"
|
||||||
|
version = "2.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "07709a6d4eba90ab10ec170a0530b3aafc81cb8a2d380e4423ae41fc55fe5745"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"thiserror 2.0.17",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "new_debug_unreachable"
|
name = "new_debug_unreachable"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ tauri-build = { version = "2.5.1", features = [] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0.228" }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
tauri = { version = "2.9.2", features = [] }
|
tauri = { version = "2.9.2", features = [] }
|
||||||
tauri-plugin-log = "2"
|
tauri-plugin-log = "2"
|
||||||
|
network-interface = "2.0.3"
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
mod network;
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
@@ -11,6 +13,9 @@ pub fn run() {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
network::get_network_interfaces
|
||||||
|
])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
app_lib::run();
|
app_lib::run();
|
||||||
}
|
}
|
||||||
|
|||||||
37
src-tauri/src/network.rs
Normal file
37
src-tauri/src/network.rs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
use network_interface::{NetworkInterface, NetworkInterfaceConfig};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct InterfaceInfo {
|
||||||
|
pub name: String,
|
||||||
|
pub addr: String, // IP 地址
|
||||||
|
pub mac: Option<String>, // MAC 地址,可能不存在
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn map_interface_to_info(interface: &NetworkInterface) -> InterfaceInfo {
|
||||||
|
let mac_address = interface.mac_addr.as_ref().map(|mac| mac.to_string());
|
||||||
|
let ip_addr = interface.addr.iter().find(|addr| addr.ip().is_ipv4())
|
||||||
|
.map(|addr| addr.ip().to_string())
|
||||||
|
.unwrap_or_else(|| "N/A".to_string());
|
||||||
|
|
||||||
|
InterfaceInfo {
|
||||||
|
name: interface.name.clone(),
|
||||||
|
addr: ip_addr,
|
||||||
|
mac: mac_address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_network_interfaces() -> Result<Vec<InterfaceInfo>, String> {
|
||||||
|
match NetworkInterface::show() {
|
||||||
|
Ok(interfaces) => {
|
||||||
|
let info_list: Vec<InterfaceInfo> = interfaces.into_iter()
|
||||||
|
.map(|interface| map_interface_to_info(&interface))
|
||||||
|
.collect();
|
||||||
|
Ok(info_list)
|
||||||
|
}
|
||||||
|
Err(e) => Err(format!("无法获取网络接口信息: {}", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,14 +15,8 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
<Header/>
|
<Header/>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
|
|
||||||
<!-- 1. 侧边栏: 固定宽度,全高 -->
|
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<!-- 2. 主内容区域: 占据剩余所有空间 (flex-1) -->
|
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
<!-- 渲染子路由的内容 (+page.svelte 或下级 +layout.svelte)
|
|
||||||
使用标准的 <slot /> 替换了 {@render children()} -->
|
|
||||||
|
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,79 @@
|
|||||||
<script>
|
<script lang="ts">
|
||||||
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
|
||||||
|
// 定义与 Rust 端 InterfaceInfo 匹配的 TypeScript 类型
|
||||||
|
interface InterfaceInfo {
|
||||||
|
name: string;
|
||||||
|
addr: string;
|
||||||
|
mac?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let interfaces: InterfaceInfo[] = [];
|
||||||
|
let loading: boolean = true;
|
||||||
|
let error: string | null = null;
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
try {
|
||||||
|
// 调用 Rust 后端命令
|
||||||
|
interfaces = await invoke<InterfaceInfo[]>("get_network_interfaces");
|
||||||
|
console.log("Network interfaces:", interfaces);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to get network interfaces:", err);
|
||||||
|
// 错误处理,将错误信息展示给用户
|
||||||
|
error = (err as string) || "未知错误";
|
||||||
|
} finally {
|
||||||
|
loading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<div class="network-info">
|
||||||
|
<h2>🌐 网络接口信息</h2>
|
||||||
|
|
||||||
</style>
|
{#if loading}
|
||||||
|
<p>正在加载...</p>
|
||||||
<div>
|
{:else if error}
|
||||||
IP 工具箱
|
<p class="error-message">错误: {error}</p>
|
||||||
|
{:else}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>名称</th>
|
||||||
|
<th>IP 地址 (IPv4)</th>
|
||||||
|
<th>MAC 地址</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{#each interfaces as iface (iface.name)}
|
||||||
|
<tr>
|
||||||
|
<td><strong>{iface.name}</strong></td>
|
||||||
|
<td>{iface.addr}</td>
|
||||||
|
<td>{iface.mac || 'N/A'}</td>
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* 简单的 Svelte 样式 */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: var(--main-bg-color);
|
||||||
|
}
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user