A:前端增加数据绑定展示

This commit is contained in:
jimmy committed 2025-06-27 14:04:43 +08:00
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'
}
+5
View File
@@ -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}
+3 -5
View File
@@ -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 {