loading status
This commit is contained in:
parent
d9befa634a
commit
9c025628ff
|
@ -4,26 +4,33 @@ const availableActions = ref<Record<string, { label: string; allowedTypes: numbe
|
|||
|
||||
export function useKompasActions() {
|
||||
const { sendCommandToPython, isReady } = usePythonBridge()
|
||||
const loading = ref(false)
|
||||
const error = ref<string | null>(null)
|
||||
|
||||
/**
|
||||
* Загружает список действий с бэкенда
|
||||
*/
|
||||
async function loadActions() {
|
||||
loading.value = true
|
||||
if (!isReady.value) {
|
||||
console.warn('Bridge не готов')
|
||||
return
|
||||
}
|
||||
|
||||
error.value = null
|
||||
|
||||
try {
|
||||
const actions = await sendCommandToPython<{
|
||||
[key: string]: { label: string; allowed_types: number[] }
|
||||
// Отправляем команду и ожидаем ответ в новом формате
|
||||
const response = await sendCommandToPython<{
|
||||
status: 'success' | 'error'
|
||||
data: Record<string, { label: string; allowed_types: number[] }> | null
|
||||
error: string | null
|
||||
}>('get_available_actions')
|
||||
|
||||
if (actions) {
|
||||
// Преобразуем все ключи `allowed_types` → `allowedTypes`
|
||||
if (response.status === 'success' && response.data) {
|
||||
// Преобразуем данные
|
||||
const convertedActions: Record<string, { label: string; allowedTypes: number[] }> = {}
|
||||
|
||||
for (const key in actions) {
|
||||
const { label, allowed_types } = actions[key]
|
||||
for (const key in response.data) {
|
||||
const { label, allowed_types } = response.data[key]
|
||||
convertedActions[key] = {
|
||||
label,
|
||||
allowedTypes: allowed_types
|
||||
|
@ -31,18 +38,18 @@ export function useKompasActions() {
|
|||
}
|
||||
|
||||
availableActions.value = convertedActions
|
||||
} else {
|
||||
throw new Error(response.error || 'Неизвестная ошибка')
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('Ошибка при получении действий:', err)
|
||||
error.value = 'Не удалось загрузить список действий'
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
availableActions,
|
||||
loading,
|
||||
error,
|
||||
loadActions,
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ref, onMounted } from 'vue'
|
||||
// composables/usePythonBridge.ts
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
@ -12,29 +12,25 @@ declare global {
|
|||
new(transport: any, ready: (channel: any) => void): void
|
||||
}
|
||||
}
|
||||
|
||||
interface PythonEvent {
|
||||
type: string
|
||||
data: any
|
||||
}
|
||||
|
||||
const pyjs = ref<any>(null)
|
||||
const isReady = ref<boolean>(false)
|
||||
|
||||
// Хранилище обработчиков событий
|
||||
const pythonEventHandlers = new Map<string, Array<(data: any) => void>>()
|
||||
|
||||
function receiveFromPython(eventType: string, data: any) {
|
||||
console.warn("🟢 Event from Python:", eventType, data)
|
||||
|
||||
const handlers = pythonEventHandlers.get(eventType) || []
|
||||
handlers.forEach(handler => handler(data))
|
||||
}
|
||||
|
||||
export function usePythonBridge() {
|
||||
const pyjs = useState<any>('bridge_pyjs', () => null)
|
||||
const isReady = useState<boolean>('bridge_ready', () => false)
|
||||
const isAvailableOnPlatform = useState<boolean>('bridge_av', () => false)
|
||||
const loading = useState<boolean>('bridge_loading', () => false)
|
||||
|
||||
// Хранилище обработчиков событий
|
||||
const pythonEventHandlers = new Map<string, Array<(data: any) => void>>()
|
||||
|
||||
function receiveFromPython(eventType: string, data: any) {
|
||||
console.warn("🟢 Event from Python:", eventType, data)
|
||||
const handlers = pythonEventHandlers.get(eventType) || []
|
||||
handlers.forEach(handler => handler(data))
|
||||
}
|
||||
|
||||
function setupQWebChannel() {
|
||||
if (window.QWebChannel && window.qt && window.qt.webChannelTransport) {
|
||||
new window.QWebChannel(window.qt.webChannelTransport, function (channel) {
|
||||
isAvailableOnPlatform.value = true
|
||||
isReady.value = true
|
||||
pyjs.value = channel.objects.pyjs
|
||||
|
||||
|
@ -47,6 +43,7 @@ export function usePythonBridge() {
|
|||
}
|
||||
})
|
||||
} else {
|
||||
isAvailableOnPlatform.value = false
|
||||
console.error('Qt WebChannel недоступен')
|
||||
}
|
||||
}
|
||||
|
@ -55,24 +52,34 @@ export function usePythonBridge() {
|
|||
command: string,
|
||||
data: Record<string, any> = {}
|
||||
): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!isReady.value) {
|
||||
reject(new Error('Мост еще не готов'))
|
||||
return
|
||||
}
|
||||
if (!isReady.value) {
|
||||
throw new Error('Мост еще не готов')
|
||||
}
|
||||
|
||||
if (!pyjs.value || !pyjs.value.callFromJS) {
|
||||
reject(new Error('Python недоступен'))
|
||||
return
|
||||
}
|
||||
if (!pyjs.value || !pyjs.value.callFromJS) {
|
||||
throw new Error('Python недоступен')
|
||||
}
|
||||
|
||||
pyjs.value.callFromJS(command, JSON.stringify(data), (result: string) => {
|
||||
loading.value = true
|
||||
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
try {
|
||||
resolve(JSON.parse(result))
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
pyjs.value.callFromJS(command, JSON.stringify(data), (result: string) => {
|
||||
try {
|
||||
const parsed = JSON.parse(result)
|
||||
resolve(parsed)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
reject(err)
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
}, 300)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -96,7 +103,7 @@ export function usePythonBridge() {
|
|||
}
|
||||
|
||||
const checkBridge = () => {
|
||||
if (window.QWebChannel && window.qt?.webChannelTransport) {
|
||||
if (import.meta.client && window.QWebChannel && window.qt?.webChannelTransport) {
|
||||
setupQWebChannel()
|
||||
}
|
||||
|
||||
|
@ -107,12 +114,16 @@ export function usePythonBridge() {
|
|||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// Вызываем один раз при первом монтировании
|
||||
if (!isReady.value) {
|
||||
checkBridge()
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
pyjs,
|
||||
isReady,
|
||||
isAvailableOnPlatform,
|
||||
loading,
|
||||
sendCommandToPython,
|
||||
onPythonEvent,
|
||||
offPythonEvent,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
|
||||
const { sendCommandToPython, isReady } = usePythonBridge()
|
||||
const { sendCommandToPython, isReady, isAvailableOnPlatform, loading } = usePythonBridge()
|
||||
|
||||
const {
|
||||
documents,
|
||||
|
@ -21,28 +21,48 @@ watch(selectedAction, () => {
|
|||
selectedTypes.value = availableActions.value[selectedAction.value].allowedTypes
|
||||
})
|
||||
|
||||
const resultData = ref<any>(null) // <-- Новое поле для результата
|
||||
|
||||
// Синхронизация с КОМПАС
|
||||
async function syncKompas() {
|
||||
try {
|
||||
const docs = await sendCommandToPython<KompasDocument[]>('open_kompas')
|
||||
// Получаем ответ в новом формате: { status, data, error }
|
||||
const response = await sendCommandToPython('open_kompas')
|
||||
|
||||
if (!docs || !Array.isArray(docs)) {
|
||||
console.warn('Неожиданный ответ от Python:', docs)
|
||||
documents.value = []
|
||||
} else {
|
||||
documents.value = docs
|
||||
loadActions()
|
||||
console.log('Полный ответ от Python:', response)
|
||||
|
||||
// Проверяем структуру ответа
|
||||
if (!response || typeof response !== 'object') {
|
||||
throw new Error('Некорректный формат ответа: пустой или не объект')
|
||||
}
|
||||
console.warn('Документы из КОМПАС:', docs)
|
||||
|
||||
if (response.status === 'error') {
|
||||
throw new Error(response.error || 'Неизвестная ошибка')
|
||||
}
|
||||
|
||||
if (!response.data || !Array.isArray(response.data)) {
|
||||
throw new Error('Данные от сервера отсутствуют или имеют неверный формат')
|
||||
}
|
||||
|
||||
// Успешно получили документы
|
||||
documents.value = response.data
|
||||
loadActions()
|
||||
|
||||
console.warn('Документы из КОМПАС:', documents.value)
|
||||
|
||||
} catch (err) {
|
||||
console.error('Ошибка при синхронизации с КОМПАС:', err)
|
||||
alert('Не удалось получить список документов из КОМПАС')
|
||||
alert(`Не удалось получить список документов из КОМПАС\n(${err.message})`)
|
||||
}
|
||||
}
|
||||
const canRunAction = computed(() => {
|
||||
return selectedAction.value && filteredDocuments.value.length > 0
|
||||
})
|
||||
|
||||
function updateUIWithResult(data: any) {
|
||||
resultData.value = data
|
||||
}
|
||||
|
||||
async function runSelectedAction() {
|
||||
if (!selectedAction.value) {
|
||||
alert('Выберите действие')
|
||||
|
@ -53,18 +73,32 @@ async function runSelectedAction() {
|
|||
// Отправляем команду на бэкенд
|
||||
const result = await sendCommandToPython(selectedAction.value)
|
||||
|
||||
console.log('Результат выполнения:', result)
|
||||
console.log('Полный ответ от сервера:', result)
|
||||
|
||||
if (result && result.status === 'success') {
|
||||
alert(`Действие "${selectedAction.value}" выполнено успешно!`)
|
||||
} else if (result && result.status === 'error') {
|
||||
alert(`Ошибка при выполнении: ${result.error || 'Неизвестная ошибка'}`)
|
||||
} else {
|
||||
alert('Не удалось получить результат от сервера')
|
||||
// Проверяем, что результат — это объект и содержит поле status
|
||||
if (typeof result !== 'object' || result === null) {
|
||||
// alert('Ошибка: получен некорректный формат данных от сервера')
|
||||
console.error('Некорректный формат результата:', result)
|
||||
return
|
||||
}
|
||||
|
||||
// Теперь можно безопасно работать с result.status
|
||||
if (result.status === 'success') {
|
||||
// alert(`✅ Действие "${selectedAction.value}" выполнено успешно!`)
|
||||
updateUIWithResult(result.data) // например, выводим данные в интерфейс
|
||||
}
|
||||
else if (result.status === 'error') {
|
||||
const errorMessage = result.error || 'Неизвестная ошибка'
|
||||
// alert(`❌ Ошибка при выполнении: ${errorMessage}`)
|
||||
console.error('Ошибка от сервера:', errorMessage)
|
||||
}
|
||||
else {
|
||||
// alert('⚠️ Не удалось обработать результат от сервера')
|
||||
console.warn('Неизвестный статус ответа:', result)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Ошибка при выполнении действия:', err)
|
||||
alert('Произошла ошибка при выполнении действия')
|
||||
console.error('🚨 Ошибка при выполнении действия:', err)
|
||||
alert('🔥 Произошла критическая ошибка при выполнении действия')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -72,20 +106,29 @@ async function runSelectedAction() {
|
|||
<template>
|
||||
<div class="flex flex-col gap-4 p-4">
|
||||
<h1>Компас — прослушка событий от Python</h1>
|
||||
|
||||
<div v-if="!isAvailableOnPlatform" class="mb-4 p-3 bg-red-100 text-red-800 rounded">
|
||||
❌ <strong>Мост недоступен на этой платформе.</strong> Возможно, вы запускаете приложение не в Qt/PySide.
|
||||
</div>
|
||||
<!-- Статус -->
|
||||
<div v-if="!isReady" class="mb-4 p-3 bg-yellow-100 text-yellow-800 rounded flex items-center">
|
||||
<div v-if="!isReady && isAvailableOnPlatform"
|
||||
class="mb-4 p-3 bg-yellow-100 text-yellow-800 rounded flex items-center">
|
||||
⚠️ <strong class="ml-2">Ещё рано отправлять запросы. Идёт инициализация...</strong>
|
||||
</div>
|
||||
<template v-else>
|
||||
<template v-if="isReady && isAvailableOnPlatform">
|
||||
{{ loading }}
|
||||
<div>
|
||||
<!-- Кнопка синхронизации -->
|
||||
<button @click="syncKompas"
|
||||
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded mb-4 transition">
|
||||
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded mb-4 transition"
|
||||
:disabled="loading">
|
||||
🔁 Синхронизировать КОМПАС
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ resultData }}
|
||||
</div>
|
||||
|
||||
<!-- Сообщение о пустом списке -->
|
||||
<div v-if="!documents.length" class="text-gray-500 italic mt-2">
|
||||
Нет данных о документах. Нажмите "Синхронизировать КОМПАС".
|
||||
|
@ -117,7 +160,7 @@ async function runSelectedAction() {
|
|||
</label>
|
||||
</div>
|
||||
<!-- Кнопка выполнения действия -->
|
||||
<button type="button" @click="runSelectedAction" :disabled="!canRunAction" class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600
|
||||
<button type="button" @click="runSelectedAction" :disabled="!canRunAction || loading" class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600
|
||||
disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none
|
||||
transition">
|
||||
✅ Выполнить действие
|
||||
|
|
Loading…
Reference in New Issue