mirror of
https://github.com/ZeroCatDev/Classworks.git
synced 2025-12-07 21:13:11 +00:00
删除不需要的文件
This commit is contained in:
parent
6c990bd8e4
commit
fb20f8a3ea
@ -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>
|
|
||||||
@ -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 {
|
|
||||||
// 生成32位随机通知ID
|
|
||||||
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>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user