1
0
mirror of https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git synced 2025-08-14 00:25:46 +02:00

add websockets support

This commit is contained in:
MoonTestUse1
2025-01-05 01:23:12 +06:00
parent 787d19528b
commit 7997184442
4 changed files with 307 additions and 2 deletions

View File

@@ -0,0 +1,74 @@
import { ref } from 'vue'
class WebSocketClient {
private socket: WebSocket | null = null
private reconnectAttempts = 0
private maxReconnectAttempts = 5
private reconnectTimeout = 3000
private messageHandlers: ((data: any) => void)[] = []
// Состояние подключения
isConnected = ref(false)
connect(type: 'admin' | 'employee', id?: number) {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
const baseUrl = `${protocol}//${window.location.host}`
const url = type === 'admin' ?
`${baseUrl}/api/requests/ws/admin` :
`${baseUrl}/api/requests/ws/employee/${id}`
this.socket = new WebSocket(url)
this.socket.onopen = () => {
console.log('WebSocket connected')
this.isConnected.value = true
this.reconnectAttempts = 0
}
this.socket.onmessage = (event) => {
const data = JSON.parse(event.data)
this.messageHandlers.forEach(handler => handler(data))
}
this.socket.onclose = () => {
console.log('WebSocket disconnected')
this.isConnected.value = false
this.tryReconnect()
}
this.socket.onerror = (error) => {
console.error('WebSocket error:', error)
this.isConnected.value = false
}
}
private tryReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++
setTimeout(() => {
console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.maxReconnectAttempts})`)
this.connect('admin')
}, this.reconnectTimeout)
}
}
addMessageHandler(handler: (data: any) => void) {
this.messageHandlers.push(handler)
}
removeMessageHandler(handler: (data: any) => void) {
const index = this.messageHandlers.indexOf(handler)
if (index > -1) {
this.messageHandlers.splice(index, 1)
}
}
disconnect() {
if (this.socket) {
this.socket.close()
this.socket = null
}
}
}
export const wsClient = new WebSocketClient()

View File

@@ -0,0 +1,152 @@
<template>
<div class="p-6">
<div class="mb-6">
<h1 class="text-2xl font-bold mb-4">Панель администратора</h1>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-2">Новые заявки</h3>
<p class="text-3xl font-bold text-blue-600">{{ statistics.by_status?.NEW || 0 }}</p>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-2">В работе</h3>
<p class="text-3xl font-bold text-yellow-600">{{ statistics.by_status?.IN_PROGRESS || 0 }}</p>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-2">Завершено</h3>
<p class="text-3xl font-bold text-green-600">{{ statistics.by_status?.COMPLETED || 0 }}</p>
</div>
<div class="bg-white p-4 rounded-lg shadow">
<h3 class="text-lg font-semibold mb-2">Отклонено</h3>
<p class="text-3xl font-bold text-red-600">{{ statistics.by_status?.REJECTED || 0 }}</p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h2 class="text-xl font-bold mb-4">Последние заявки</h2>
<div class="overflow-x-auto">
<table class="min-w-full table-auto">
<thead>
<tr class="bg-gray-100">
<th class="px-4 py-2 text-left">ID</th>
<th class="px-4 py-2 text-left">Сотрудник</th>
<th class="px-4 py-2 text-left">Тип</th>
<th class="px-4 py-2 text-left">Приоритет</th>
<th class="px-4 py-2 text-left">Статус</th>
<th class="px-4 py-2 text-left">Дата создания</th>
<th class="px-4 py-2 text-left">Действия</th>
</tr>
</thead>
<tbody>
<tr v-for="request in requests" :key="request.id" class="border-b">
<td class="px-4 py-2">#{{ request.id }}</td>
<td class="px-4 py-2">{{ request.employee_name }}</td>
<td class="px-4 py-2">{{ request.request_type }}</td>
<td class="px-4 py-2">
<span :class="getPriorityClass(request.priority)">
{{ request.priority }}
</span>
</td>
<td class="px-4 py-2">
<span :class="getStatusClass(request.status)">
{{ request.status }}
</span>
</td>
<td class="px-4 py-2">{{ formatDate(request.created_at) }}</td>
<td class="px-4 py-2">
<button @click="openRequestDetails(request)" class="text-blue-600 hover:text-blue-800">
Подробнее
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import axios from '@/plugins/axios'
import { wsClient } from '@/plugins/websocket'
import { formatDate } from '@/utils/date'
const requests = ref([])
const statistics = ref({
total: 0,
by_status: {}
})
// Загрузка данных
const fetchData = async () => {
try {
const [requestsResponse, statsResponse] = await Promise.all([
axios.get('/api/requests/admin'),
axios.get('/api/requests/statistics')
])
requests.value = requestsResponse.data
statistics.value = statsResponse.data
} catch (error) {
console.error('Error fetching data:', error)
}
}
// Обработчик WebSocket сообщений
const handleWebSocketMessage = (data: any) => {
if (data.type === 'new_request') {
// Обновляем статистику при получении новой заявки
statistics.value.total++
statistics.value.by_status.NEW = (statistics.value.by_status.NEW || 0) + 1
// Добавляем новую заявку в начало списка
requests.value.unshift(data.data)
} else if (data.type === 'status_update') {
// Обновляем статус заявки в списке
const request = requests.value.find(r => r.id === data.data.id)
if (request) {
const oldStatus = request.status
request.status = data.data.status
// Обновляем статистику
statistics.value.by_status[oldStatus]--
statistics.value.by_status[data.data.status] = (statistics.value.by_status[data.data.status] || 0) + 1
}
}
}
// Подключение к WebSocket при монтировании компонента
onMounted(() => {
fetchData()
wsClient.connect('admin')
wsClient.addMessageHandler(handleWebSocketMessage)
})
// Отключение от WebSocket при размонтировании компонента
onUnmounted(() => {
wsClient.removeMessageHandler(handleWebSocketMessage)
wsClient.disconnect()
})
const getPriorityClass = (priority: string) => {
const classes = {
HIGH: 'text-red-600',
MEDIUM: 'text-yellow-600',
LOW: 'text-green-600'
}
return classes[priority] || ''
}
const getStatusClass = (status: string) => {
const classes = {
NEW: 'text-blue-600',
IN_PROGRESS: 'text-yellow-600',
COMPLETED: 'text-green-600',
REJECTED: 'text-red-600'
}
return classes[status] || ''
}
const openRequestDetails = (request: any) => {
// Здесь можно добавить логику открытия модального окна с деталями заявки
console.log('Opening request details:', request)
}
</script>