A:剩余diamagnetic提交
This commit is contained in:
1 parent
04bb8099ac
commit
0981ef5d3e
8 files changed
+541
No files matched your search
@@ -0,0 +1,213 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Data;
|
||||
using Data.Models;
|
||||
|
||||
namespace Shell.Utils;
|
||||
|
||||
public static class NetworkApiHelper
|
||||
{
|
||||
private static readonly Dictionary<string, object> _networkInterfaceCache = new();
|
||||
private static DateTime _cacheLastUpdateTime = DateTime.MinValue;
|
||||
private static readonly TimeSpan _cacheExpireTime = TimeSpan.FromMinutes(5);
|
||||
|
||||
public static Dictionary<string, object> NetworkInterfaceCache => _networkInterfaceCache;
|
||||
public static DateTime CacheLastUpdateTime => _cacheLastUpdateTime;
|
||||
public static TimeSpan CacheExpireTime => _cacheExpireTime;
|
||||
|
||||
public static void UpdateCacheTime() => _cacheLastUpdateTime = DateTime.Now;
|
||||
|
||||
public static async Task<List<GlobalNetworkAggregatedBase>> GetAllNetworkCardsHourlyDataAsync(int hours)
|
||||
{
|
||||
var result = new List<GlobalNetworkAggregatedBase>();
|
||||
|
||||
try
|
||||
{
|
||||
var allHourlyData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByHourAsync("", hours);
|
||||
var networkGuids = allHourlyData.Select(x => "").Distinct();
|
||||
|
||||
foreach (var guid in networkGuids)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(guid))
|
||||
result.Add(new GlobalNetworkHourly { NetworkCardGuid = guid });
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果出错,返回空列表
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<List<GlobalNetworkAggregatedBase>> GetAllNetworkCardsDailyDataAsync(int days)
|
||||
{
|
||||
var result = new List<GlobalNetworkAggregatedBase>();
|
||||
|
||||
try
|
||||
{
|
||||
var allDailyData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByDayAsync("", days);
|
||||
var networkGuids = allDailyData.Select(x => "").Distinct();
|
||||
|
||||
foreach (var guid in networkGuids)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(guid))
|
||||
result.Add(new GlobalNetworkDaily { NetworkCardGuid = guid });
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果出错,返回空列表
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async Task<bool> HasNetworkDataInTimeRangeAsync(string dataType, long startTime)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (dataType)
|
||||
{
|
||||
case "hourly":
|
||||
var hourlyData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByHourAsync("", 1);
|
||||
return hourlyData.Any();
|
||||
case "daily":
|
||||
var dailyData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByDayAsync("", 1);
|
||||
return dailyData.Any();
|
||||
default:
|
||||
var networkData = await DatabaseManager.Instance.Networks.GetGlobalNetworkHistoryAsync("", 1);
|
||||
return networkData.Any();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<List<object>> GetTrafficTrendsAsync(string timeRange, string interfaceId, long startTime, long endTime)
|
||||
{
|
||||
var points = new List<object>();
|
||||
|
||||
try
|
||||
{
|
||||
if (timeRange == "hour")
|
||||
{
|
||||
if (interfaceId == "all")
|
||||
{
|
||||
var allData = await DatabaseManager.Instance.Networks
|
||||
.GetGlobalNetworkByTimeRangeAsync("", startTime, endTime);
|
||||
var groupedData = allData.GroupBy(d => d.Timestep).Select(g => new
|
||||
{
|
||||
timestamp = g.Key.ToString(),
|
||||
uploadSpeed = g.Sum(x => x.Upload),
|
||||
downloadSpeed = g.Sum(x => x.Download)
|
||||
});
|
||||
|
||||
points.AddRange(groupedData);
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = await DatabaseManager.Instance.Networks.GetGlobalNetworkByTimeRangeAsync(interfaceId,
|
||||
startTime, endTime);
|
||||
points.AddRange(data.Select(d => new
|
||||
{
|
||||
timestamp = d.Timestep.ToString(),
|
||||
uploadSpeed = d.Upload,
|
||||
downloadSpeed = d.Download
|
||||
}));
|
||||
}
|
||||
}
|
||||
else if (timeRange == "day")
|
||||
{
|
||||
if (interfaceId == "all")
|
||||
{
|
||||
var allData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByHourAsync("", 24);
|
||||
var groupedData = allData.GroupBy(d => d.HourTimestamp).Select(g => new
|
||||
{
|
||||
timestamp = g.Key.ToString(),
|
||||
uploadSpeed = g.Sum(x => x.AvgUpload),
|
||||
downloadSpeed = g.Sum(x => x.AvgDownload)
|
||||
});
|
||||
|
||||
points.AddRange(groupedData);
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = await DatabaseManager.Instance.Networks.GetGlobalNetworkByHourAsync(interfaceId, 24);
|
||||
points.AddRange(data.Select(d => new
|
||||
{
|
||||
timestamp = d.HourTimestamp.ToString(),
|
||||
uploadSpeed = d.AvgUpload,
|
||||
downloadSpeed = d.AvgDownload
|
||||
}));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int days = timeRange == "week" ? 7 : 30;
|
||||
if (interfaceId == "all")
|
||||
{
|
||||
var allData = await DatabaseManager.Instance.Networks.GetGlobalNetworkByDayAsync("", days);
|
||||
var groupedData = allData.GroupBy(d => d.DayTimestamp).Select(g => new
|
||||
{
|
||||
timestamp = g.Key.ToString(),
|
||||
uploadSpeed = g.Sum(x => x.AvgUpload),
|
||||
downloadSpeed = g.Sum(x => x.AvgDownload)
|
||||
});
|
||||
|
||||
points.AddRange(groupedData);
|
||||
}
|
||||
else
|
||||
{
|
||||
var data = await DatabaseManager.Instance.Networks.GetGlobalNetworkByDayAsync(interfaceId, days);
|
||||
points.AddRange(data.Select(d => new
|
||||
{
|
||||
timestamp = d.DayTimestamp.ToString(),
|
||||
uploadSpeed = d.AvgUpload,
|
||||
downloadSpeed = d.AvgDownload
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 如果出错,返回空数据
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
public static string GetPortServiceName(int port)
|
||||
{
|
||||
return port switch
|
||||
{
|
||||
21 => "FTP",
|
||||
22 => "SSH",
|
||||
23 => "Telnet",
|
||||
25 => "SMTP",
|
||||
53 => "DNS",
|
||||
80 => "HTTP",
|
||||
110 => "POP3",
|
||||
123 => "NTP",
|
||||
143 => "IMAP",
|
||||
443 => "HTTPS",
|
||||
993 => "IMAPS",
|
||||
995 => "POP3S",
|
||||
1433 => "SQL Server",
|
||||
3306 => "MySQL",
|
||||
3389 => "RDP",
|
||||
5432 => "PostgreSQL",
|
||||
6379 => "Redis",
|
||||
8080 => "HTTP-Alt",
|
||||
8443 => "HTTPS-Alt",
|
||||
_ when port >= 1024 && port <= 5000 => "User Port",
|
||||
_ when port > 5000 && port < 32768 => "Service Port",
|
||||
_ when port >= 32768 => "Dynamic Port",
|
||||
_ => "Unknown"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Common.Logger;
|
||||
|
||||
namespace Shell.Utils;
|
||||
|
||||
/// <summary>
|
||||
/// Windows开机自启动辅助类
|
||||
/// </summary>
|
||||
public static class WindowsStartupHelper
|
||||
{
|
||||
private const string REGISTRY_KEY = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
|
||||
private const string APP_NAME = "NetVizor";
|
||||
|
||||
/// <summary>
|
||||
/// 设置开机自启动
|
||||
/// </summary>
|
||||
/// <param name="enable">是否启用自启动</param>
|
||||
/// <returns>操作是否成功</returns>
|
||||
public static bool SetStartup(bool enable)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, true);
|
||||
if (key == null)
|
||||
{
|
||||
Log.Error4Ctx("无法打开注册表项");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
{
|
||||
// 获取当前执行文件的路径
|
||||
var executablePath = Assembly.GetExecutingAssembly().Location;
|
||||
|
||||
// 如果是.NET Core/.NET 5+应用,使用exe文件路径
|
||||
if (executablePath.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
executablePath = Path.ChangeExtension(executablePath, ".exe");
|
||||
}
|
||||
|
||||
// 如果exe文件不存在,尝试从当前目录查找
|
||||
if (!File.Exists(executablePath))
|
||||
{
|
||||
var currentDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
var exeName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + ".exe";
|
||||
executablePath = Path.Combine(currentDir ?? "", exeName);
|
||||
}
|
||||
|
||||
if (!File.Exists(executablePath))
|
||||
{
|
||||
Log.Error4Ctx($"找不到可执行文件: {executablePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加自启动项
|
||||
key.SetValue(APP_NAME, $"\"{executablePath}\"");
|
||||
Log.Info($"已设置开机自启动: {executablePath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 删除自启动项
|
||||
if (key.GetValue(APP_NAME) != null)
|
||||
{
|
||||
key.DeleteValue(APP_NAME);
|
||||
Log.Info("已取消开机自启动");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error4Ctx($"设置开机自启动失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否已设置开机自启动
|
||||
/// </summary>
|
||||
/// <returns>是否已设置自启动</returns>
|
||||
public static bool IsStartupEnabled()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, false);
|
||||
if (key == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var value = key.GetValue(APP_NAME)?.ToString();
|
||||
return !string.IsNullOrEmpty(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning($"检查开机自启动状态失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前设置的启动路径
|
||||
/// </summary>
|
||||
/// <returns>启动路径,如果未设置则返回null</returns>
|
||||
public static string? GetStartupPath()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(REGISTRY_KEY, false);
|
||||
if (key == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = key.GetValue(APP_NAME)?.ToString();
|
||||
return value?.Trim('"'); // 移除引号
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Warning($"获取开机自启动路径失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { globalIgnores } from 'eslint/config';
|
||||
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
|
||||
import pluginVue from 'eslint-plugin-vue';
|
||||
import pluginVitest from '@vitest/eslint-plugin';
|
||||
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import vuePlugin from 'eslint-plugin-vue';
|
||||
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
||||
import prettier from 'eslint-config-prettier';
|
||||
export default defineConfigWithVueTs({
|
||||
name: 'app/files-to-lint',
|
||||
files: ['**/*.{ts,mts,tsx,vue}']
|
||||
}, globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']), pluginVue.configs['flat/essential'], vueTsConfigs.recommended,
|
||||
// 重要:确保自定义规则配置优先级更高
|
||||
{
|
||||
rules: {
|
||||
// 完全禁用未使用变量的检查 - 更强力的方式
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'no-unused-vars': 'off'
|
||||
// 或者使用你之前的配置(二选一)
|
||||
/*
|
||||
'@typescript-eslint/no-unused-vars': ['warn', {
|
||||
'varsIgnorePattern': '^_',
|
||||
'argsIgnorePattern': '^_',
|
||||
// 添加更多宽松配置
|
||||
'ignoreRestSiblings': true,
|
||||
'caughtErrors': 'none'
|
||||
}],
|
||||
*/
|
||||
}
|
||||
}, {
|
||||
...pluginVitest.configs.recommended,
|
||||
files: ['src/**/__tests__/*'],
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
window: 'readonly',
|
||||
document: 'readonly'
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
vue: vuePlugin,
|
||||
'@typescript-eslint': tsPlugin
|
||||
},
|
||||
rules: {
|
||||
// ❗ 严重错误(保留)
|
||||
'no-undef': 'error',
|
||||
'vue/no-unused-components': 'warn',
|
||||
'@typescript-eslint/no-explicit-any': 'warn',
|
||||
// ✅ 风格类(宽松)
|
||||
'vue/multi-word-component-names': 'off',
|
||||
'vue/max-attributes-per-line': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
// ✅ 开发方便
|
||||
'no-console': 'off',
|
||||
'no-debugger': 'warn'
|
||||
}
|
||||
}, {
|
||||
...prettier
|
||||
}, skipFormatting);
|
||||
@@ -0,0 +1,36 @@
|
||||
import type { IBridge, LogParam, MessagePayload } from './IBridge'
|
||||
|
||||
export class CSharpBridge implements IBridge {
|
||||
private eventListeners = new Map<string, Set<(...args: any[]) => void>>()
|
||||
|
||||
log(param: LogParam): void {
|
||||
console.log(`[${param.level.toUpperCase()}] ${param.message}`, param.data)
|
||||
}
|
||||
|
||||
async invokeMethod(methodName: string, ...args: any[]): Promise<any> {
|
||||
// 默认实现
|
||||
console.warn(`CSharpBridge.invokeMethod not implemented: ${methodName}`, args)
|
||||
return Promise.resolve(null)
|
||||
}
|
||||
|
||||
addEventListener(eventName: string, callback: (...args: any[]) => void): void {
|
||||
if (!this.eventListeners.has(eventName)) {
|
||||
this.eventListeners.set(eventName, new Set())
|
||||
}
|
||||
this.eventListeners.get(eventName)!.add(callback)
|
||||
}
|
||||
|
||||
removeEventListener(eventName: string, callback: (...args: any[]) => void): void {
|
||||
const listeners = this.eventListeners.get(eventName)
|
||||
if (listeners) {
|
||||
listeners.delete(callback)
|
||||
}
|
||||
}
|
||||
|
||||
protected emit(eventName: string, ...args: any[]): void {
|
||||
const listeners = this.eventListeners.get(eventName)
|
||||
if (listeners) {
|
||||
listeners.forEach(callback => callback(...args))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// 桥接接口定义
|
||||
export interface IBridge {
|
||||
log(param: LogParam): void
|
||||
invokeMethod(methodName: string, ...args: any[]): Promise<any>
|
||||
addEventListener(eventName: string, callback: (...args: any[]) => void): void
|
||||
removeEventListener(eventName: string, callback: (...args: any[]) => void): void
|
||||
}
|
||||
|
||||
export interface LogParam {
|
||||
level: 'info' | 'warn' | 'error' | 'debug'
|
||||
message: string
|
||||
data?: any
|
||||
}
|
||||
|
||||
export interface MessagePayload<T = any> {
|
||||
type: string
|
||||
data: T
|
||||
timestamp?: number
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { CSharpBridge } from './CSharpBridge'
|
||||
import type { LogParam } from './IBridge'
|
||||
|
||||
export class TauriBridge extends CSharpBridge {
|
||||
async invokeMethod(methodName: string, ...args: any[]): Promise<any> {
|
||||
try {
|
||||
// 如果运行在 Tauri 环境中
|
||||
if (window.__TAURI__) {
|
||||
const { invoke } = window.__TAURI__.tauri
|
||||
return await invoke(methodName, ...args)
|
||||
}
|
||||
|
||||
// 否则使用父类的默认实现
|
||||
return super.invokeMethod(methodName, ...args)
|
||||
} catch (error) {
|
||||
console.error(`TauriBridge.invokeMethod error:`, error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
log(param: LogParam): void {
|
||||
// 在 Tauri 环境中,可以使用更高级的日志记录
|
||||
super.log(param)
|
||||
}
|
||||
}
|
||||
|
||||
// 扩展 Window 接口
|
||||
declare global {
|
||||
interface Window {
|
||||
__TAURI__?: {
|
||||
tauri: {
|
||||
invoke: (cmd: string, ...args: any[]) => Promise<any>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
declare const _default: import("vite").UserConfigFnObject;
|
||||
export default _default;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd());
|
||||
return {
|
||||
plugins: [
|
||||
vue(),
|
||||
AutoImport({
|
||||
imports: [
|
||||
'vue',
|
||||
{
|
||||
'naive-ui': [
|
||||
'useDialog',
|
||||
'useMessage',
|
||||
'useNotification',
|
||||
'useLoadingBar'
|
||||
]
|
||||
}
|
||||
]
|
||||
}),
|
||||
Components({
|
||||
resolvers: [NaiveUiResolver()]
|
||||
})
|
||||
],
|
||||
// 防止 Vite 掩盖 Rust 错误
|
||||
clearScreen: false,
|
||||
server: {
|
||||
port: 3000,
|
||||
strictPort: true,
|
||||
host: "0.0.0.0",
|
||||
watch: {},
|
||||
},
|
||||
build: {},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
Reference in new issue
Block a user