243 lines
10 KiB
Vue
243 lines
10 KiB
Vue
<script setup lang="ts">
|
||
|
||
const { sendCommandToPython, isReady, isAvailableOnPlatform, loading } = usePythonBridge()
|
||
|
||
const {
|
||
documents,
|
||
selectedTypes,
|
||
documentCounts,
|
||
filteredDocuments,
|
||
documentTypeLabels,
|
||
getDocumentLabel,
|
||
} = useKompasDocuments()
|
||
|
||
const {
|
||
availableActions,
|
||
loadActions
|
||
} = useKompasActions()
|
||
|
||
const selectedAction = ref()
|
||
watch(selectedAction, () => {
|
||
selectedTypes.value = availableActions.value[selectedAction.value].allowedTypes
|
||
})
|
||
|
||
const statsData = ref(null)
|
||
const filesData = ref(null)
|
||
const drawingData = ref(null)
|
||
const resultData = ref(null)
|
||
|
||
// Синхронизация с КОМПАС
|
||
async function syncKompas() {
|
||
try {
|
||
// Получаем ответ в новом формате: { status, data, error }
|
||
const response = await sendCommandToPython('open_kompas')
|
||
|
||
console.log('Полный ответ от Python:', response)
|
||
|
||
// Проверяем структуру ответа
|
||
if (!response || typeof response !== 'object') {
|
||
throw new Error('Некорректный формат ответа: пустой или не объект')
|
||
}
|
||
|
||
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(`Не удалось получить список документов из КОМПАС\n(${err})`)
|
||
}
|
||
}
|
||
const canRunAction = computed(() => {
|
||
return selectedAction.value && filteredDocuments.value.length > 0
|
||
})
|
||
|
||
function updateUIWithResult(data: any, action: any) {
|
||
// Обнуляем предыдущие данные
|
||
statsData.value = null
|
||
filesData.value = null
|
||
drawingData.value = null
|
||
resultData.value = null
|
||
|
||
// Сохраняем сырые данные на случай непредвиденного типа действия
|
||
resultData.value = data
|
||
|
||
if (!data || !data.result) {
|
||
console.warn('Некорректный формат данных')
|
||
return
|
||
}
|
||
|
||
console.warn(action)
|
||
console.warn(JSON.stringify(data))
|
||
switch (action) {
|
||
case 'stats':
|
||
// Для статистики
|
||
statsData.value = data
|
||
break
|
||
|
||
case 'iges':
|
||
case 'export_raster':
|
||
filesData.value = data
|
||
break
|
||
|
||
case 'project_support':
|
||
drawingData.value = data
|
||
break
|
||
|
||
default:
|
||
console.warn(`Неизвестное действие: ${action}`)
|
||
break
|
||
}
|
||
}
|
||
|
||
async function runSelectedAction() {
|
||
if (!selectedAction.value) {
|
||
alert('Выберите действие')
|
||
return
|
||
}
|
||
|
||
try {
|
||
// Отправляем команду на бэкенд
|
||
// Обнуляем предыдущие данные
|
||
statsData.value = null
|
||
filesData.value = null
|
||
drawingData.value = null
|
||
resultData.value = null
|
||
|
||
const result = await sendCommandToPython(selectedAction.value)
|
||
|
||
console.log('Полный ответ от сервера:', result)
|
||
|
||
// Проверяем, что результат — это объект и содержит поле status
|
||
if (typeof result !== 'object' || result === null) {
|
||
// alert('Ошибка: получен некорректный формат данных от сервера')
|
||
console.error('Некорректный формат результата:', result)
|
||
return
|
||
}
|
||
|
||
// Теперь можно безопасно работать с result.status
|
||
if (result.status === 'success') {
|
||
// alert(`✅ Действие "${selectedAction.value}" выполнено успешно!`)
|
||
updateUIWithResult(result.data, selectedAction.value) // например, выводим данные в интерфейс
|
||
}
|
||
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('🔥 Произошла критическая ошибка при выполнении действия')
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<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 && isAvailableOnPlatform"
|
||
class="mb-4 p-3 bg-yellow-100 text-yellow-800 rounded flex items-center">
|
||
⚠️ <strong class="ml-2">Ещё рано отправлять запросы. Идёт инициализация...</strong>
|
||
</div>
|
||
<template v-if="isReady && isAvailableOnPlatform">
|
||
<div>
|
||
<!-- Кнопка синхронизации -->
|
||
<button @click="syncKompas"
|
||
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded mb-4 transition"
|
||
:disabled="loading">
|
||
🔁 Синхронизировать КОМПАС
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Сообщение о пустом списке -->
|
||
<div v-if="!documents.length" class="text-gray-500 italic mt-2">
|
||
Нет данных о документах. Нажмите "Синхронизировать КОМПАС".
|
||
</div>
|
||
<template v-else>
|
||
<!-- Фильтры и действия в одной строке -->
|
||
<div class="grid grid-cols-2 gap-6">
|
||
|
||
<!-- Чекбоксы для фильтрации -->
|
||
<div class="mb-4 p-3 bg-gray-100 rounded shadow-sm">
|
||
<h3 class="font-medium mb-2">Фильтр по типам:</h3>
|
||
<div class="flex flex-wrap gap-4">
|
||
<label v-for="(label, code) in documentTypeLabels" :key="code"
|
||
class="flex items-center gap-1">
|
||
<input type="checkbox" :value="Number(code)" v-model="selectedTypes"
|
||
:disabled="documentCounts[Number(code)] === 0"
|
||
class="form-checkbox h-4 w-4 text-blue-600 disabled:opacity-50" />
|
||
<span class="text-sm">
|
||
{{ label }} ({{ documentCounts[Number(code)] }})
|
||
</span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Действия -->
|
||
<div class="mb-4 p-3 bg-gray-100 rounded shadow-sm">
|
||
<h3 class="font-medium mb-2">Действия:</h3>
|
||
<div class="flex flex-col gap-2">
|
||
<label v-for="(action, key) in availableActions" :key="key" class="flex items-center gap-2">
|
||
<input type="radio" :value="key" v-model="selectedAction"
|
||
class="form-radio h-4 w-4 text-blue-600" />
|
||
<span class="text-sm">{{ action.label }}</span>
|
||
</label>
|
||
</div>
|
||
<!-- Кнопка выполнения действия -->
|
||
<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">
|
||
✅ Выполнить действие
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Грид документов -->
|
||
<div v-if="filteredDocuments.length" class="mt-4 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||
<div v-for="(doc, index) in filteredDocuments" :key="index"
|
||
class="border rounded shadow-sm p-4 hover:shadow-md transition-shadow bg-white">
|
||
<h3 class="font-medium text-lg">{{ doc.name }}</h3>
|
||
|
||
<div class="mt-2">
|
||
<strong>Тип:</strong> {{ getDocumentLabel(doc.type) }}
|
||
</div>
|
||
|
||
<div class="mt-2 text-xs text-gray-500 truncate">
|
||
{{ doc.path }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<template v-if="filesData">
|
||
<TableFiles :files="(filesData as any).result" />
|
||
</template>
|
||
<template v-if="statsData">
|
||
<TableStats :statsData="(statsData as any).result"/>
|
||
</template>
|
||
<template v-if="drawingData">
|
||
<TableDrawing :drawingData="(drawingData as any).result" />
|
||
</template>
|
||
</div>
|
||
</template>
|
||
</template>
|
||
</div>
|
||
</template> |