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 注册
|
// http 注册
|
||||||
import httpPlugin from './plugins/http'
|
import httpPlugin from './plugins/http'
|
||||||
|
import { useApplicationStore } from '@/stores/application'
|
||||||
|
|
||||||
|
// 注册消息监听
|
||||||
|
const useApplicationStore1 = useApplicationStore()
|
||||||
|
useApplicationStore1.subscribe()
|
||||||
|
|
||||||
// httpPlugin.install(app)
|
// 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
|
private heartbeatInterval = 30000
|
||||||
|
|
||||||
// 可配置参数
|
// 可配置参数
|
||||||
maxDelay = 30000 // 最大重连间隔 30s
|
maxDelay = 10000 // 最大重连间隔 30s
|
||||||
baseDelay = 500 // 初始间隔 0.5s
|
baseDelay = 500 // 初始间隔 0.5s
|
||||||
reconnectAttempts = 0
|
reconnectAttempts = 0
|
||||||
isManuallyClosed = false
|
isManuallyClosed = false
|
||||||
@@ -83,7 +83,7 @@ export class WebSocketManager {
|
|||||||
else console.error('收到WebSocket 欢迎信息,但是解析失败!!')
|
else console.error('收到WebSocket 欢迎信息,但是解析失败!!')
|
||||||
})
|
})
|
||||||
this.registerHandler('pong', (data: WebSocketResponse) => {
|
this.registerHandler('pong', (data: WebSocketResponse) => {
|
||||||
console.log("接收到服务器心跳");
|
console.log('接收到服务器心跳')
|
||||||
})
|
})
|
||||||
|
|
||||||
this.socket.onopen = () => {
|
this.socket.onopen = () => {
|
||||||
@@ -105,7 +105,6 @@ export class WebSocketManager {
|
|||||||
this.socket.onmessage = (event) => {
|
this.socket.onmessage = (event) => {
|
||||||
try {
|
try {
|
||||||
const message: WebSocketResponse = JSON.parse(event.data)
|
const message: WebSocketResponse = JSON.parse(event.data)
|
||||||
console.log('收到消息:', message)
|
|
||||||
this.handleMessage(message)
|
this.handleMessage(message)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error parsing WebSocket message:', error)
|
console.error('Error parsing WebSocket message:', error)
|
||||||
@@ -162,7 +161,6 @@ export class WebSocketManager {
|
|||||||
this.initialize(data)
|
this.initialize(data)
|
||||||
} else {
|
} else {
|
||||||
console.error('获取WebSocket路径失败')
|
console.error('获取WebSocket路径失败')
|
||||||
this.isReconnecting.value = false
|
|
||||||
// 继续重试
|
// 继续重试
|
||||||
this.retryConnect()
|
this.retryConnect()
|
||||||
}
|
}
|
||||||
@@ -172,8 +170,8 @@ export class WebSocketManager {
|
|||||||
this.initialize(this.url)
|
this.initialize(this.url)
|
||||||
} else {
|
} else {
|
||||||
console.error('无法获取WebSocket连接地址')
|
console.error('无法获取WebSocket连接地址')
|
||||||
this.isReconnecting.value = false
|
|
||||||
}
|
}
|
||||||
|
this.isReconnecting.value = false
|
||||||
}
|
}
|
||||||
}, delay)
|
}, delay)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,35 +19,44 @@
|
|||||||
<!-- 应用列表 -->
|
<!-- 应用列表 -->
|
||||||
<div class="app-list">
|
<div class="app-list">
|
||||||
<div
|
<div
|
||||||
v-for="app in applications"
|
v-for="app in useApplicationStore1.appInfos"
|
||||||
:key="app.id"
|
:key="app.Id"
|
||||||
class="app-item"
|
class="app-item"
|
||||||
:class="{ 'app-item--selected': selectedApp?.id === app.id }"
|
:class="{ 'app-item--selected': selectedApp?.Id === app.Id }"
|
||||||
@click="selectApp(app)"
|
@click="selectApp(app)"
|
||||||
@mouseenter="handleMouseEnter"
|
@mouseenter="handleMouseEnter"
|
||||||
@mouseleave="handleMouseLeave"
|
@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">
|
<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>
|
||||||
|
|
||||||
<!-- 应用信息 -->
|
<!-- 应用信息 -->
|
||||||
<div class="app-info">
|
<div class="app-info">
|
||||||
<div class="app-name">{{ app.name }}</div>
|
<div class="app-name">{{ app.ProductName }}</div>
|
||||||
<div class="app-details">
|
<div class="app-details">
|
||||||
<span class="app-detail">PID: {{ app.pid }}</span>
|
<span class="app-detail">线程数: {{ app.ProcessIds.length }}</span>
|
||||||
<span class="app-detail">内存: {{ app.memory }}</span>
|
<span class="app-detail">内存: {{ formatSpeed(app.UseMemory) }}</span>
|
||||||
<span class="app-detail">{{ app.status }}</span>
|
<span class="app-detail">{{ !!app.ExitCode ? '已退出' : '活动' }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 应用状态指示器 -->
|
<!-- 应用状态指示器 -->
|
||||||
<div class="app-status">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,7 +78,12 @@ import { storeToRefs } from 'pinia'
|
|||||||
import { CheckmarkOutline, DesktopOutline } from '@vicons/ionicons5'
|
import { CheckmarkOutline, DesktopOutline } from '@vicons/ionicons5'
|
||||||
import { httpClient } from '@/utils/http.ts'
|
import { httpClient } from '@/utils/http.ts'
|
||||||
import { ResponseModel, SubscriptionInfo } from '@/types/response'
|
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 {
|
interface Application {
|
||||||
id: string
|
id: string
|
||||||
@@ -90,24 +104,45 @@ const emit = defineEmits<{
|
|||||||
}>()
|
}>()
|
||||||
const webSocketStore = useWebSocketStore()
|
const webSocketStore = useWebSocketStore()
|
||||||
const { isOpen } = storeToRefs(webSocketStore)
|
const { isOpen } = storeToRefs(webSocketStore)
|
||||||
console.log(isOpen);
|
watch(
|
||||||
watch(isOpen,(oldValue, newValue) => {
|
isOpen,
|
||||||
if (oldValue || newValue) {
|
(oldValue, newValue) => {
|
||||||
console.log("触发!!!!!!!!");
|
if (oldValue || newValue) {
|
||||||
// 发送请求【请求订阅软件列表】
|
// 发送请求【请求订阅软件列表】
|
||||||
// const subAppInfo: SubscriptionInfo = {
|
const subAppInfo: SubscriptionInfo = {
|
||||||
// subscriptionType: 'ApplicationInfo',
|
subscriptionType: 'ApplicationInfo',
|
||||||
// interval: 1000,
|
interval: 1000,
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// httpClient.post(`/subscribe`, JSON.stringify(subAppInfo)).then((res: ResponseModel) => {
|
httpClient.post(`/subscribe`, JSON.stringify(subAppInfo)).then((res: ResponseModel) => {
|
||||||
// console.log(res)
|
console.log(res)
|
||||||
// })
|
})
|
||||||
}
|
}
|
||||||
},{immediate:true})
|
},
|
||||||
|
{ 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[]>([
|
const applications = ref<Application[]>([
|
||||||
@@ -118,7 +153,7 @@ const applications = ref<Application[]>([
|
|||||||
pid: 12456,
|
pid: 12456,
|
||||||
memory: '245.6 MB',
|
memory: '245.6 MB',
|
||||||
status: '活跃',
|
status: '活跃',
|
||||||
statusType: 'active',
|
statupe: 'active',
|
||||||
path: '/Applications/Visual Studio Code.app',
|
path: '/Applications/Visual Studio Code.app',
|
||||||
windowTitle: 'main.js - myproject',
|
windowTitle: 'main.js - myproject',
|
||||||
cpuUsage: 5.2,
|
cpuUsage: 5.2,
|
||||||
@@ -223,6 +258,25 @@ const handleAction = (type: string) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<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 {
|
.folded-corner {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -645,6 +699,7 @@ const handleAction = (type: string) => {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-secondary);
|
color: var(--text-secondary);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
z-index: 90;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-badge {
|
.sidebar-badge {
|
||||||
|
|||||||
Reference in new issue
Block a user