1
0
mirror of https://github.com/ZeroCatDev/Classworks.git synced 2025-12-08 13:49:37 +00:00

删除不需要的文件

This commit is contained in:
Sunwuyuan 2025-11-23 17:08:39 +08:00
parent 6c990bd8e4
commit fb20f8a3ea
No known key found for this signature in database
GPG Key ID: A6A54CF66F56BB64
2 changed files with 0 additions and 826 deletions

View File

@ -1,435 +0,0 @@
<template>
<v-container fluid>
<v-row>
<v-col cols="12">
<v-card>
<v-card-title>
<v-icon class="mr-2">mdi-transit-connection-variant</v-icon>
Socket.IO 新事件系统测试
</v-card-title>
<v-card-text>
<p class="text-body-2 mb-4">
此页面用于测试新的通用事件转发系统支持聊天KV变化等各种事件类型
</p>
<!-- 连接状态 -->
<v-alert
:type="connected ? 'success' : 'error'"
:text="`连接状态: ${connected ? '已连接' : '未连接'} | Socket ID: ${socketId || '-'}`"
class="mb-4"
/>
<!-- 发送测试事件 -->
<v-card outlined class="mb-4">
<v-card-title class="text-h6">发送测试事件</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12" md="6">
<v-select
v-model="testEventType"
:items="eventTypes"
label="事件类型"
outlined
/>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="testContent"
label="事件内容"
outlined
placeholder="输入测试内容"
/>
</v-col>
</v-row>
<v-btn
:disabled="!testEventType || !testContent.trim()"
color="primary"
@click="sendTestEvent"
>
<v-icon start>mdi-send</v-icon>
发送测试事件
</v-btn>
</v-card-text>
</v-card>
<!-- 事件统计 -->
<v-row class="mb-4">
<v-col cols="12" md="3">
<v-card color="primary" dark>
<v-card-text>
<div class="text-h4">{{ eventStats.total }}</div>
<div>总事件数</div>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="3">
<v-card color="success" dark>
<v-card-text>
<div class="text-h4">{{ eventStats.chat }}</div>
<div>聊天事件</div>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="3">
<v-card color="info" dark>
<v-card-text>
<div class="text-h4">{{ eventStats.kvChanged }}</div>
<div>KV变化事件</div>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" md="3">
<v-card color="warning" dark>
<v-card-text>
<div class="text-h4">{{ eventStats.other }}</div>
<div>其他事件</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- 实时事件日志 -->
<v-card outlined>
<v-card-title>
实时事件日志
<v-spacer/>
<v-btn
size="small"
variant="text"
@click="clearEvents"
>
<v-icon>mdi-delete</v-icon>
清空
</v-btn>
</v-card-title>
<v-card-text>
<div class="event-log">
<div
v-for="(event, index) in recentEvents"
:key="index"
class="event-item mb-3"
>
<v-card
:color="getEventColor(event.type)"
variant="outlined"
>
<v-card-text class="pa-3">
<div class="d-flex align-center mb-2">
<v-chip
:color="getEventColor(event.type)"
size="small"
variant="flat"
>
{{ event.type }}
</v-chip>
<v-spacer/>
<span class="text-caption">{{ formatTime(event.timestamp) }}</span>
</div>
<div v-if="event.senderInfo" class="mb-2">
<strong>发送者:</strong> {{ formatDeviceInfo(event.senderInfo) }}
<v-chip
v-if="isRealtimeEvent(event)"
color="purple"
size="x-small"
class="ml-2"
>
实时同步
</v-chip>
</div>
<div class="mb-2">
<strong>内容:</strong>
</div>
<pre class="text-caption">{{ JSON.stringify(event.content, null, 2) }}</pre>
<div v-if="event.eventId" class="text-caption mt-2 text-grey">
事件ID: {{ event.eventId }}
</div>
</v-card-text>
</v-card>
</div>
<div v-if="recentEvents.length === 0" class="text-center text-grey pa-4">
暂无事件
</div>
</div>
</v-card-text>
</v-card>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
</template>
<script setup>
import { ref, reactive, onMounted, onBeforeUnmount, computed } from 'vue'
import { getSocket, on as socketOn, joinToken } from '@/utils/socketClient'
import {
sendEvent,
DeviceEventTypes,
formatDeviceInfo,
isRealtimeEvent,
createDeviceEventHandler,
sendChatMessage
} from '@/utils/deviceEvents'
import { getSetting } from '@/utils/settings'
//
const connected = ref(false)
const socketId = ref('')
const testEventType = ref('chat')
const testContent = ref('')
const recentEvents = ref([])
//
const eventStats = reactive({
total: 0,
chat: 0,
kvChanged: 0,
other: 0
})
//
const eventTypes = [
{ title: '聊天消息', value: 'chat' },
{ title: 'KV变化', value: 'kv-key-changed' },
{ title: '自定义事件', value: 'custom-event' }
]
//
let cleanupFunctions = []
//
const formattedEvents = computed(() => {
return recentEvents.value.map(event => ({
...event,
formattedTime: formatTime(event.timestamp),
formattedSender: event.senderInfo ? formatDeviceInfo(event.senderInfo) : '未知'
}))
})
//
function sendTestEvent() {
if (!testEventType.value || !testContent.value.trim()) return
try {
if (testEventType.value === 'chat') {
// 使
sendChatMessage(testContent.value)
} else {
// 使
const content = testEventType.value === 'kv-key-changed' ?
{
key: 'test-key',
action: 'upsert',
value: testContent.value
} :
{ message: testContent.value }
sendEvent(testEventType.value, content)
}
testContent.value = ''
} catch (error) {
console.error('发送测试事件失败:', error)
}
}
function addEvent(eventData) {
recentEvents.value.unshift(eventData)
//
eventStats.total++
if (eventData.type === DeviceEventTypes.CHAT) {
eventStats.chat++
} else if (eventData.type === DeviceEventTypes.KV_KEY_CHANGED) {
eventStats.kvChanged++
} else {
eventStats.other++
}
//
if (recentEvents.value.length > 50) {
recentEvents.value = recentEvents.value.slice(0, 50)
}
}
function clearEvents() {
recentEvents.value = []
eventStats.total = 0
eventStats.chat = 0
eventStats.kvChanged = 0
eventStats.other = 0
}
function getEventColor(eventType) {
switch (eventType) {
case DeviceEventTypes.CHAT:
return 'success'
case DeviceEventTypes.KV_KEY_CHANGED:
return 'info'
default:
return 'warning'
}
}
function formatTime(timestamp) {
try {
const date = new Date(timestamp)
return date.toLocaleTimeString('zh-CN', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
} catch (error) {
return timestamp
}
}
//
onMounted(() => {
// socket
const socket = getSocket()
connected.value = socket.connected
socketId.value = socket.id || ''
//
socket.on('connect', () => {
connected.value = true
socketId.value = socket.id || ''
})
socket.on('disconnect', () => {
connected.value = false
socketId.value = ''
})
// token
const token = getSetting('server.kvToken')
if (token) {
joinToken(token)
}
//
const offLegacyChat = socketOn('chat:message', (msg) => {
addEvent({
type: 'chat:message',
content: msg,
timestamp: msg.at || new Date().toISOString(),
senderId: msg.senderId,
uuid: msg.uuid,
eventId: `legacy-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
})
})
//
const offDirectChat = socketOn('chat', (eventData) => {
if (eventData && eventData.content) {
addEvent({
type: 'chat',
content: eventData.content,
timestamp: eventData.timestamp,
eventId: eventData.eventId,
senderId: eventData.senderId,
senderInfo: eventData.senderInfo
})
}
})
const offLegacyKv = socketOn('kv-key-changed', (eventData) => {
//
if (eventData.content && eventData.timestamp) {
addEvent({
type: 'kv-key-changed',
content: eventData.content,
timestamp: eventData.timestamp,
eventId: eventData.eventId,
senderId: eventData.senderId,
senderInfo: eventData.senderInfo
})
} else {
//
addEvent({
type: 'kv-key-changed',
content: eventData,
timestamp: eventData.updatedAt || new Date().toISOString(),
uuid: eventData.uuid,
eventId: `legacy-kv-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
})
}
})
//
const deviceEventHandler = createDeviceEventHandler({
onChat: (chatMsg, originalEvent) => {
addEvent(originalEvent)
},
onKvChanged: (kvMsg, originalEvent) => {
addEvent(originalEvent)
},
onOtherEvent: (eventData) => {
addEvent(eventData)
},
enableLegacySupport: true
})
const offDeviceEvent = socketOn('device-event', deviceEventHandler)
//
cleanupFunctions = [
offLegacyChat,
offDirectChat,
offLegacyKv,
offDeviceEvent
]
})
onBeforeUnmount(() => {
//
try {
cleanupFunctions.forEach(cleanup => {
try {
if (typeof cleanup === 'function') {
cleanup()
}
} catch (error) {
console.warn('事件监听器清理失败:', error)
}
})
} catch (error) {
console.error('组件清理失败:', error)
}
//
cleanupFunctions = []
})
</script>
<style scoped>
.event-log {
max-height: 600px;
overflow-y: auto;
}
.event-item {
transition: all 0.3s ease;
}
.event-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
pre {
background: rgba(0,0,0,0.05);
padding: 8px;
border-radius: 4px;
white-space: pre-wrap;
word-break: break-all;
max-height: 200px;
overflow-y: auto;
}
</style>

View File

@ -1,391 +0,0 @@
<template>
<div class="urgent-test-page">
<v-container>
<v-row>
<v-col cols="12">
<v-card>
<v-card-title>
<v-icon class="mr-2">
mdi-alert-octagon
</v-icon>
紧急通知测试页面
</v-card-title>
<v-card-text>
<v-form>
<v-row>
<v-col
cols="12"
md="6"
>
<v-switch
v-model="notificationForm.isUrgent"
label="加急通知"
color="red"
inset
>
<template #prepend>
<v-icon :color="notificationForm.isUrgent ? 'red' : 'grey'">
{{ notificationForm.isUrgent ? 'mdi-alert-circle' : 'mdi-information' }}
</v-icon>
</template>
</v-switch>
</v-col>
<v-col cols="12">
<v-textarea
v-model="notificationForm.message"
label="通知内容"
outlined
rows="3"
placeholder="请输入紧急通知的内容..."
/>
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-card-actions class="px-6 pb-6">
<v-btn
:color="notificationForm.isUrgent ? 'red' : 'blue'"
:disabled="!notificationForm.message.trim()"
:loading="sending"
size="large"
variant="elevated"
@click="sendNotification"
>
<v-icon left>
{{ notificationForm.isUrgent ? 'mdi-alert-circle' : 'mdi-information' }}
</v-icon>
{{ notificationForm.isUrgent ? '发送紧急通知' : '发送通知' }}
</v-btn>
<v-spacer />
<v-btn
color="grey"
variant="outlined"
@click="resetForm"
>
重置表单
</v-btn>
</v-card-actions>
</v-card>
</v-col>
</v-row>
<!-- 消息发送历史 -->
<v-row class="mt-4">
<v-col cols="12">
<v-card>
<v-card-title>
<v-icon class="mr-2">
mdi-history
</v-icon>
消息记录
<v-spacer />
</v-card-title>
<v-card-text>
<div
v-if="sentMessages.length === 0"
class="text-center text-grey py-8"
>
<v-icon
size="64"
color="grey-lighten-2"
>
mdi-message-outline
</v-icon>
<div class="mt-2">
暂无发送记录
</div>
</div>
<v-row v-else>
<v-col
v-for="message in sentMessages.slice().reverse()"
:key="message.id"
cols="12"
md="6"
lg="4"
>
<!-- 主消息卡片 -->
<v-card
:color="getMainCardColor(message.receipts)"
class="mb-2"
>
<v-card-text>
<div class="d-flex align-center mb-2">
<span class="font-weight-medium">
{{ message.isUrgent ? '紧急通知' : '普通通知' }}
</span>
<v-spacer />
<span class="text-caption font-weight-medium">
{{ getReceiptStatus(message.receipts) }}
</span>
</div>
<div
class="text-body-2 mb-3"
style="max-height: 60px; overflow: hidden;"
>
{{ message.message }}
</div>
<div class="text-caption">
<div>发送时间{{ formatTime(message.timestamp) }}</div>
<div>事件ID{{ message.id }}</div>
<div>通知ID{{ message.notificationId }}</div>
</div>
</v-card-text>
</v-card>
<!-- 设备回执小卡片 -->
<div v-if="hasAnyReceipts(message.receipts)">
<!-- 已读设备 -->
<v-card
v-for="device in message.receipts.read"
:key="`${device.senderId}-read`"
color="success"
class="mb-1"
size="small"
>
<v-card-text class="pa-2">
<div class="align-center">
<span class="text-body-2 font-weight-medium">{{ device.deviceName }} </span>
<br/>
{{ device.deviceType }}
</div>
<div class="text-caption mt-1">
已读于 {{ formatDeviceTime(device.timestamp) }}
</div>
</v-card-text>
</v-card>
<!-- 已显示设备排除已读的设备 -->
<v-card
v-for="device in getDisplayedOnlyDevices(message.receipts)"
:key="`${device.senderId}-displayed`"
color="info-lighten-4"
variant="outlined"
class="mb-1"
size="small"
>
<v-card-text class="pa-2">
<div class="align-center">
<span class="text-body-2 font-weight-medium">{{ device.deviceName }}</span>
<v-spacer />
<span class="text-caption text-grey">
{{ device.deviceType }}
</span>
</div>
<div class="text-caption text-grey mt-1">
已显示于 {{ formatDeviceTime(device.timestamp) }}
</div>
</v-card-text>
</v-card>
</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-container>
<ChatWidget />
<EventSender ref="eventSender" />
</div>
</template>
<script>
import ChatWidget from '@/components/ChatWidget.vue'
import EventSender from '@/components/EventSender.vue'
import { on as socketOn } from '@/utils/socketClient'
export default {
name: 'UrgentNotificationTest',
components: {
ChatWidget,
EventSender
},
data() {
return {
sending: false,
notificationForm: {
isUrgent: false,
message: ''
},
sentMessages: [],
receiptCleanup: []
}
},
mounted() {
this.setupEventListeners()
},
beforeUnmount() {
this.cleanup()
},
methods: {
generateNotificationId() {
// 32
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
let result = ''
for (let i = 0; i < 32; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length))
}
return result
},
async sendNotification() {
if (!this.notificationForm.message.trim()) return
this.sending = true
try {
// 32ID
const notificationId = this.generateNotificationId()
const result = await this.$refs.eventSender.sendNotification(
this.notificationForm.message,
this.notificationForm.isUrgent,
[],
{ deviceName: '测试设备', deviceType: 'system', isReadOnly: false },
notificationId
)
const eventId = result?.eventId || `msg-${Date.now()}`
this.sentMessages.push({
id: eventId,
notificationId: notificationId,
message: this.notificationForm.message,
isUrgent: this.notificationForm.isUrgent,
timestamp: new Date().toISOString(),
receipts: {
displayed: [],
read: []
}
})
console.log('通知已发送事件ID:', eventId, '通知ID:', notificationId)
this.resetForm()
} catch (error) {
console.error('发送通知失败:', error)
} finally {
this.sending = false
}
},
resetForm() {
this.notificationForm = {
isUrgent: false,
message: ''
}
},
setupEventListeners() {
//
const cleanup1 = socketOn('notification-displayed', (data) => {
console.log('收到显示回执:', data)
this.updateReceipt(data, 'displayed')
})
//
const cleanup2 = socketOn('notification-read', (data) => {
console.log('收到已读回执:', data)
this.updateReceipt(data, 'read')
})
this.receiptCleanup.push(cleanup1, cleanup2)
},
updateReceipt(data, type) {
const originalEventId = data.originalEventId
const notificationId = data.notificationId || data.content?.notificationId
if (!originalEventId && !notificationId) return
const message = this.sentMessages.find(msg =>
msg.id === originalEventId ||
msg.notificationId === notificationId
)
if (message) {
// 使 senderInfo senderId
const deviceInfo = {
senderId: data.senderId || 'unknown-sender',
deviceName: data.senderInfo?.deviceName || data.deviceInfo?.deviceName || '未知设备',
deviceType: data.senderInfo?.deviceType || data.deviceInfo?.deviceType || 'unknown',
timestamp: new Date().toISOString()
}
// senderId
const exists = message.receipts[type].find(item =>
item.senderId === deviceInfo.senderId
)
if (!exists) {
message.receipts[type].push(deviceInfo)
console.log(`更新${type}回执:`, message.id, deviceInfo)
}
}
},
cleanup() {
this.receiptCleanup.forEach(cleanup => cleanup())
this.receiptCleanup = []
},
formatTime(timestamp) {
return new Date(timestamp).toLocaleString('zh-CN')
},
getReceiptStatus(receipts) {
if (receipts.read.length > 0) return '已读'
if (receipts.displayed.length > 0) return '已显示'
return '已发送'
},
getReceiptColor(receipts) {
if (receipts.read.length > 0) return 'success'
if (receipts.displayed.length > 0) return 'info'
return 'grey'
},
formatDeviceTime(timestamp) {
return new Date(timestamp).toLocaleTimeString('zh-CN')
},
getMainCardColor(receipts) {
// 绿
if (receipts.read.length > 0) return 'success'
if (receipts.displayed.length > 0) return 'info'
return 'grey'
},
hasAnyReceipts(receipts) {
return receipts.read.length > 0 || receipts.displayed.length > 0
},
getDisplayedOnlyDevices(receipts) {
// senderId
const readSenderIds = receipts.read.map(device => device.senderId)
return receipts.displayed.filter(device =>
!readSenderIds.includes(device.senderId)
)
}
}
}
</script>
<style scoped>
.gap-1 {
gap: 4px;
}
.message-history-card .v-chip {
margin: 1px;
}
</style>