A:前端增加数据绑定展示
This commit is contained in:
1 parent
26182b28d4
commit
d6ccf139e2
7 files changed
+191
-33
No files matched your search
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Time:2025/3/19 21:59 41
|
||||
* Name:constants.ts
|
||||
* Path:src/constants
|
||||
* ProjectName:argus-src
|
||||
* Author:charlatans
|
||||
*
|
||||
* Il n'ya qu'un héroïsme au monde :
|
||||
* c'est de voir le monde tel qu'il est et de l'aimer.
|
||||
*/
|
||||
|
||||
import { FILE_SIZE_UNIT_ENUM } from '@/constants/enums'
|
||||
|
||||
|
||||
/**
|
||||
* 文件大小单位和字节的对应关系
|
||||
* @param name 单位名称
|
||||
* @param bytes 字节
|
||||
*/
|
||||
export const FILE_SIZE_UNIT_SIZE: Record<string, { name: string; bytes: number }> = {
|
||||
B: { name: FILE_SIZE_UNIT_ENUM.B, bytes: 1 },
|
||||
KB: { name: FILE_SIZE_UNIT_ENUM.KB, bytes: 1024 },
|
||||
MB: { name: FILE_SIZE_UNIT_ENUM.MB, bytes: 1024 * 1024 },
|
||||
GB: { name: FILE_SIZE_UNIT_ENUM.GB, bytes: 1024 * 1024 * 1024 },
|
||||
TB: { name: FILE_SIZE_UNIT_ENUM.TB, bytes: 1024 * 1024 * 1024 * 1024 }
|
||||
} as const
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Time:2025/3/19 21:50 33
|
||||
* Name:enums.ts
|
||||
* Path:src/constants
|
||||
* ProjectName:argus-src
|
||||
* Author:charlatans
|
||||
*
|
||||
* Il n'ya qu'un héroïsme au monde :
|
||||
* c'est de voir le monde tel qu'il est et de l'aimer.
|
||||
*/
|
||||
/**
|
||||
* 文件大小单位
|
||||
*/
|
||||
export enum FILE_SIZE_UNIT_ENUM {
|
||||
B = 'B',
|
||||
KB = 'KB',
|
||||
MB = 'MB',
|
||||
GB = 'GB',
|
||||
TB = 'TB'
|
||||
}
|
||||
@@ -36,6 +36,11 @@ httpConfig.setConfig({
|
||||
|
||||
// http 注册
|
||||
import httpPlugin from './plugins/http'
|
||||
import { useApplicationStore } from '@/stores/application'
|
||||
|
||||
// 注册消息监听
|
||||
const useApplicationStore1 = useApplicationStore()
|
||||
useApplicationStore1.subscribe()
|
||||
|
||||
// httpPlugin.install(app)
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ref, Ref, watch } from 'vue'
|
||||
import { defineStore, storeToRefs } from 'pinia'
|
||||
import { useWebSocketStore } from '@/stores/websocketStore'
|
||||
import { WebSocketResponse } from '@/types/websocket'
|
||||
import { ApplicationType } from '@/types/infoModel'
|
||||
|
||||
export const useApplicationStore = defineStore('applicationInfoSub', () => {
|
||||
const appInfos: Ref<ApplicationType[]> = ref([])
|
||||
|
||||
/**
|
||||
* 订阅信息
|
||||
*/
|
||||
function subscribe() {
|
||||
const webSocketStore = useWebSocketStore()
|
||||
const { isOpen } = storeToRefs(webSocketStore)
|
||||
watch(
|
||||
isOpen,
|
||||
(oldValue, newValue) => {
|
||||
if (oldValue || newValue) {
|
||||
webSocketStore.registerHandler('ApplicationInfo', (data: WebSocketResponse) => {
|
||||
appInfos.value = JSON.parse(data.data)
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
}
|
||||
|
||||
return { appInfos, subscribe }
|
||||
})
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* 软件信息类型
|
||||
*/
|
||||
type ApplicationType = {
|
||||
ProcessName: string
|
||||
ProcessIds: number[]
|
||||
StartTime: string
|
||||
HasExited: false
|
||||
ExitTime?: number
|
||||
ExitCode?: number
|
||||
UseMemory: number
|
||||
ThreadCount: number
|
||||
MainModulePath: string
|
||||
MainModuleName: string
|
||||
FileDescription: string
|
||||
ProductName: string
|
||||
CompanyName: string
|
||||
Version: string
|
||||
LegalCopyright: string
|
||||
IconBase64: string
|
||||
Id:string
|
||||
}
|
||||
|
||||
export {ApplicationType}
|
||||
@@ -37,7 +37,7 @@ export class WebSocketManager {
|
||||
private heartbeatInterval = 30000
|
||||
|
||||
// 可配置参数
|
||||
maxDelay = 30000 // 最大重连间隔 30s
|
||||
maxDelay = 10000 // 最大重连间隔 30s
|
||||
baseDelay = 500 // 初始间隔 0.5s
|
||||
reconnectAttempts = 0
|
||||
isManuallyClosed = false
|
||||
@@ -83,7 +83,7 @@ export class WebSocketManager {
|
||||
else console.error('收到WebSocket 欢迎信息,但是解析失败!!')
|
||||
})
|
||||
this.registerHandler('pong', (data: WebSocketResponse) => {
|
||||
console.log("接收到服务器心跳");
|
||||
console.log('接收到服务器心跳')
|
||||
})
|
||||
|
||||
this.socket.onopen = () => {
|
||||
@@ -105,7 +105,6 @@ export class WebSocketManager {
|
||||
this.socket.onmessage = (event) => {
|
||||
try {
|
||||
const message: WebSocketResponse = JSON.parse(event.data)
|
||||
console.log('收到消息:', message)
|
||||
this.handleMessage(message)
|
||||
} catch (error) {
|
||||
console.error('Error parsing WebSocket message:', error)
|
||||
@@ -162,7 +161,6 @@ export class WebSocketManager {
|
||||
this.initialize(data)
|
||||
} else {
|
||||
console.error('获取WebSocket路径失败')
|
||||
this.isReconnecting.value = false
|
||||
// 继续重试
|
||||
this.retryConnect()
|
||||
}
|
||||
@@ -172,8 +170,8 @@ export class WebSocketManager {
|
||||
this.initialize(this.url)
|
||||
} else {
|
||||
console.error('无法获取WebSocket连接地址')
|
||||
this.isReconnecting.value = false
|
||||
}
|
||||
this.isReconnecting.value = false
|
||||
}
|
||||
}, delay)
|
||||
}
|
||||
|
||||
@@ -19,35 +19,44 @@
|
||||
<!-- 应用列表 -->
|
||||
<div class="app-list">
|
||||
<div
|
||||
v-for="app in applications"
|
||||
:key="app.id"
|
||||
v-for="app in useApplicationStore1.appInfos"
|
||||
:key="app.Id"
|
||||
class="app-item"
|
||||
:class="{ 'app-item--selected': selectedApp?.id === app.id }"
|
||||
:class="{ 'app-item--selected': selectedApp?.Id === app.Id }"
|
||||
@click="selectApp(app)"
|
||||
@mouseenter="handleMouseEnter"
|
||||
@mouseleave="handleMouseLeave"
|
||||
>
|
||||
<!-- 书角折叠效果 -->
|
||||
<div v-if="selectedApp?.id === app.id" class="folded-corner"></div>
|
||||
<div v-if="selectedApp?.Id === app.Id" class="folded-corner"></div>
|
||||
|
||||
<!-- 应用图标 -->
|
||||
<div class="app-icon">
|
||||
<img :src="app.icon" :alt="app.name" />
|
||||
|
||||
<img v-if="!lodash.isEmpty(app.IconBase64)" :src="'data:image/jpeg;base64,' + app.IconBase64" :alt="app.ProductName" />
|
||||
<div v-else>
|
||||
<div class="app-icon-span">
|
||||
<span>{{getFirstByte(app.ProductName)}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 应用信息 -->
|
||||
<div class="app-info">
|
||||
<div class="app-name">{{ app.name }}</div>
|
||||
<div class="app-name">{{ app.ProductName }}</div>
|
||||
<div class="app-details">
|
||||
<span class="app-detail">PID: {{ app.pid }}</span>
|
||||
<span class="app-detail">内存: {{ app.memory }}</span>
|
||||
<span class="app-detail">{{ app.status }}</span>
|
||||
<span class="app-detail">线程数: {{ app.ProcessIds.length }}</span>
|
||||
<span class="app-detail">内存: {{ formatSpeed(app.UseMemory) }}</span>
|
||||
<span class="app-detail">{{ !!app.ExitCode ? '已退出' : '活动' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 应用状态指示器 -->
|
||||
<div class="app-status">
|
||||
<div class="status-indicator" :class="`status-${app.statusType}`"></div>
|
||||
<div
|
||||
class="status-indicator"
|
||||
:class="`status-${!!app.ExitCode ? 'active' : 'background'}`"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,7 +78,12 @@ import { storeToRefs } from 'pinia'
|
||||
import { CheckmarkOutline, DesktopOutline } from '@vicons/ionicons5'
|
||||
import { httpClient } from '@/utils/http.ts'
|
||||
import { ResponseModel, SubscriptionInfo } from '@/types/response'
|
||||
import { useWebSocketStore,getIsConnected } from '@/stores/websocketStore'
|
||||
import { useWebSocketStore, getIsConnected } from '@/stores/websocketStore'
|
||||
import { useApplicationStore } from '@/stores/application'
|
||||
import { ApplicationType } from '@/types/infoModel'
|
||||
import { convertFileSize } from '@/utils/fileUtil'
|
||||
import { FILE_SIZE_UNIT_ENUM } from '@/constants/enums'
|
||||
import lodash from 'lodash'
|
||||
// 定义应用数据类型
|
||||
interface Application {
|
||||
id: string
|
||||
@@ -90,24 +104,45 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
const webSocketStore = useWebSocketStore()
|
||||
const { isOpen } = storeToRefs(webSocketStore)
|
||||
console.log(isOpen);
|
||||
watch(isOpen,(oldValue, newValue) => {
|
||||
if (oldValue || newValue) {
|
||||
console.log("触发!!!!!!!!");
|
||||
// 发送请求【请求订阅软件列表】
|
||||
// const subAppInfo: SubscriptionInfo = {
|
||||
// subscriptionType: 'ApplicationInfo',
|
||||
// interval: 1000,
|
||||
// }
|
||||
//
|
||||
// httpClient.post(`/subscribe`, JSON.stringify(subAppInfo)).then((res: ResponseModel) => {
|
||||
// console.log(res)
|
||||
// })
|
||||
}
|
||||
},{immediate:true})
|
||||
watch(
|
||||
isOpen,
|
||||
(oldValue, newValue) => {
|
||||
if (oldValue || newValue) {
|
||||
// 发送请求【请求订阅软件列表】
|
||||
const subAppInfo: SubscriptionInfo = {
|
||||
subscriptionType: 'ApplicationInfo',
|
||||
interval: 1000,
|
||||
}
|
||||
|
||||
httpClient.post(`/subscribe`, JSON.stringify(subAppInfo)).then((res: ResponseModel) => {
|
||||
console.log(res)
|
||||
})
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
// 绑定软件信息
|
||||
const useApplicationStore1 = useApplicationStore()
|
||||
const { appInfos } = storeToRefs(useApplicationStore1)
|
||||
watch(appInfos, (oldValue, newValue) => {})
|
||||
|
||||
// 选中的应用
|
||||
const selectedApp = ref<Application | null>(null)
|
||||
const selectedApp = ref<ApplicationType | null>(null)
|
||||
|
||||
// 计算人体工学单位
|
||||
const formatSpeed = (useMemory: number) => {
|
||||
const fmt = convertFileSize(useMemory, FILE_SIZE_UNIT_ENUM.B)
|
||||
return fmt.size + fmt.unit
|
||||
}
|
||||
// 获取软件名称的第一个字
|
||||
const getFirstByte = (name:string) => {
|
||||
if (lodash.isEmpty(name)) {
|
||||
return ""
|
||||
}
|
||||
const firstChar = name.charAt(0);
|
||||
return firstChar
|
||||
}
|
||||
|
||||
// 模拟应用数据
|
||||
const applications = ref<Application[]>([
|
||||
@@ -118,7 +153,7 @@ const applications = ref<Application[]>([
|
||||
pid: 12456,
|
||||
memory: '245.6 MB',
|
||||
status: '活跃',
|
||||
statusType: 'active',
|
||||
statupe: 'active',
|
||||
path: '/Applications/Visual Studio Code.app',
|
||||
windowTitle: 'main.js - myproject',
|
||||
cpuUsage: 5.2,
|
||||
@@ -223,6 +258,25 @@ const handleAction = (type: string) => {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
/*字体图标*/
|
||||
.app-icon-span {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: linear-gradient(135deg, var(--accent-primary) 0%, #1d4ed8 100%);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
|
||||
}
|
||||
|
||||
.app-icon-span span {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* 书角折叠效果 - 平衡版 */
|
||||
.folded-corner {
|
||||
position: absolute;
|
||||
@@ -645,6 +699,7 @@ const handleAction = (type: string) => {
|
||||
font-weight: 600;
|
||||
color: var(--text-secondary);
|
||||
margin: 0;
|
||||
z-index: 90;
|
||||
}
|
||||
|
||||
.sidebar-badge {
|
||||
|
||||
Reference in new issue
Block a user