Compare commits
No commits in common. "3a386996bb7e5fb2ad04c004e060b8ed93ba5e47" and "266b34a996bee83b3b30882882d98ef530017d23" have entirely different histories.
3a386996bb
...
266b34a996
|
@ -133,12 +133,6 @@ DATABASES = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DATABASES = {
|
|
||||||
"default": {
|
|
||||||
"ENGINE": "django.db.backends.sqlite3",
|
|
||||||
"NAME": BASE_DIR / "db.sqlite3",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
// composables/useKompasActions.ts
|
|
||||||
|
|
||||||
const availableActions = ref<Record<string, { label: string; allowedTypes: number[] }>>({})
|
|
||||||
|
|
||||||
export function useKompasActions() {
|
|
||||||
const { sendCommandToPython, isReady } = usePythonBridge()
|
|
||||||
const loading = ref(false)
|
|
||||||
const error = ref<string | null>(null)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Загружает список действий с бэкенда
|
|
||||||
*/
|
|
||||||
async function loadActions() {
|
|
||||||
loading.value = true
|
|
||||||
error.value = null
|
|
||||||
try {
|
|
||||||
const actions = await sendCommandToPython<{
|
|
||||||
[key: string]: { label: string; allowed_types: number[] }
|
|
||||||
}>('get_available_actions')
|
|
||||||
|
|
||||||
if (actions) {
|
|
||||||
// Преобразуем все ключи `allowed_types` → `allowedTypes`
|
|
||||||
const convertedActions: Record<string, { label: string; allowedTypes: number[] }> = {}
|
|
||||||
|
|
||||||
for (const key in actions) {
|
|
||||||
const { label, allowed_types } = actions[key]
|
|
||||||
convertedActions[key] = {
|
|
||||||
label,
|
|
||||||
allowedTypes: allowed_types
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
availableActions.value = convertedActions
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
console.error('Ошибка при получении действий:', err)
|
|
||||||
error.value = 'Не удалось загрузить список действий'
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
availableActions,
|
|
||||||
loading,
|
|
||||||
error,
|
|
||||||
loadActions,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
// composables/useKompasDocuments.ts
|
|
||||||
|
|
||||||
const documentTypeLabels = {
|
|
||||||
0: 'Неизвестно',
|
|
||||||
1: 'Чертёж',
|
|
||||||
2: 'Фрагмент',
|
|
||||||
3: 'Спецификация',
|
|
||||||
4: 'Деталь',
|
|
||||||
5: 'Сборка',
|
|
||||||
6: 'Текстовый документ',
|
|
||||||
7: 'Технологическая сборка',
|
|
||||||
} as const
|
|
||||||
|
|
||||||
type DocumentTypeLabel = typeof documentTypeLabels
|
|
||||||
|
|
||||||
export function getDocumentLabel(typeCode: number): string {
|
|
||||||
return documentTypeLabels[typeCode as keyof DocumentTypeLabel] || 'Неизвестный тип'
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useKompasDocuments() {
|
|
||||||
// Список документов из КОМПАС
|
|
||||||
const documents = ref<KompasDocument[]>([])
|
|
||||||
|
|
||||||
// Фильтр по типам
|
|
||||||
const selectedTypes = ref<number[]>([])
|
|
||||||
|
|
||||||
// Доступные действия (динамически с бэкенда)
|
|
||||||
const availableActions = ref<Record<string, { label: string; allowedTypes: number[] }>>({})
|
|
||||||
|
|
||||||
// Вычисляем количество документов по типам
|
|
||||||
const documentCounts = computed(() => {
|
|
||||||
const counts: Record<number, number> = {}
|
|
||||||
Object.keys(documentTypeLabels).forEach(key => {
|
|
||||||
const code = Number(key)
|
|
||||||
counts[code] = documents.value.filter(doc => doc.type === code).length
|
|
||||||
})
|
|
||||||
return counts
|
|
||||||
})
|
|
||||||
|
|
||||||
// Отфильтрованный список
|
|
||||||
const filteredDocuments = computed(() => {
|
|
||||||
if (selectedTypes.value.length === 0) return documents.value
|
|
||||||
return documents.value.filter(doc => selectedTypes.value.includes(Number(doc.type)))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Какие действия сейчас применимы к документам
|
|
||||||
const applicableActions = computed(() => {
|
|
||||||
const result: Record<string, { label: string }> = {}
|
|
||||||
|
|
||||||
for (const key in availableActions.value) {
|
|
||||||
const action = availableActions.value[key]
|
|
||||||
const canApply = documents.value.some(doc => action.allowedTypes.includes(doc.type))
|
|
||||||
if (canApply) {
|
|
||||||
result[key] = { label: action.label }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
documents,
|
|
||||||
selectedTypes,
|
|
||||||
documentCounts,
|
|
||||||
filteredDocuments,
|
|
||||||
documentTypeLabels,
|
|
||||||
getDocumentLabel,
|
|
||||||
applicableActions,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -25,7 +25,7 @@ const isReady = ref<boolean>(false)
|
||||||
const pythonEventHandlers = new Map<string, Array<(data: any) => void>>()
|
const pythonEventHandlers = new Map<string, Array<(data: any) => void>>()
|
||||||
|
|
||||||
function receiveFromPython(eventType: string, data: any) {
|
function receiveFromPython(eventType: string, data: any) {
|
||||||
console.warn("🟢 Event from Python:", eventType, data)
|
console.warn("🟢 Событие из Python:", eventType, data)
|
||||||
|
|
||||||
const handlers = pythonEventHandlers.get(eventType) || []
|
const handlers = pythonEventHandlers.get(eventType) || []
|
||||||
handlers.forEach(handler => handler(data))
|
handlers.forEach(handler => handler(data))
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
|
|
||||||
type KompasDocument = {
|
|
||||||
type: number // числовой код типа документа
|
|
||||||
name: string // имя документа без расширения
|
|
||||||
path: string // путь к файлу
|
|
||||||
active: boolean // активный ли документ
|
|
||||||
}
|
|
||||||
|
|
||||||
type KompasDocumentTypeLabels = Record<number, string>
|
|
|
@ -2,152 +2,44 @@
|
||||||
|
|
||||||
const { sendCommandToPython, isReady } = usePythonBridge()
|
const { sendCommandToPython, isReady } = usePythonBridge()
|
||||||
|
|
||||||
const {
|
// Список событий для отображения
|
||||||
documents,
|
const events = ref<Array<{ type: string; data: any; timestamp: Date }>>([])
|
||||||
selectedTypes,
|
async function init() {
|
||||||
documentCounts,
|
|
||||||
filteredDocuments,
|
|
||||||
documentTypeLabels,
|
|
||||||
getDocumentLabel,
|
|
||||||
} = useKompasDocuments()
|
|
||||||
|
|
||||||
const {
|
|
||||||
availableActions,
|
|
||||||
loadActions
|
|
||||||
} = useKompasActions()
|
|
||||||
|
|
||||||
const selectedAction = ref()
|
|
||||||
watch(selectedAction, () => {
|
|
||||||
selectedTypes.value = availableActions.value[selectedAction.value].allowedTypes
|
|
||||||
})
|
|
||||||
|
|
||||||
// Синхронизация с КОМПАС
|
|
||||||
async function syncKompas() {
|
|
||||||
try {
|
try {
|
||||||
const docs = await sendCommandToPython<KompasDocument[]>('open_kompas')
|
const initResult = await sendCommandToPython<{ status: string; message: string }>("init");
|
||||||
|
console.warn("Init:", initResult);
|
||||||
|
|
||||||
if (!docs || !Array.isArray(docs)) {
|
const sheets = await sendCommandToPython<string[]>("get_sheets");
|
||||||
console.warn('Неожиданный ответ от Python:', docs)
|
console.warn("Sheets:", sheets);
|
||||||
documents.value = []
|
|
||||||
} else {
|
const data = await sendCommandToPython<string[][]>("get_data");
|
||||||
documents.value = docs
|
console.warn("Data:", data);
|
||||||
loadActions()
|
|
||||||
}
|
|
||||||
console.warn('Документы из КОМПАС:', docs)
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Ошибка при синхронизации с КОМПАС:', err)
|
console.error("Ошибка при вызове Python:", err);
|
||||||
alert('Не удалось получить список документов из КОМПАС')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const canRunAction = computed(() => {
|
|
||||||
return selectedAction.value && filteredDocuments.value.length > 0
|
watch(isReady, () => {
|
||||||
|
if (isReady.value) init()
|
||||||
})
|
})
|
||||||
|
|
||||||
async function runSelectedAction() {
|
|
||||||
if (!selectedAction.value) {
|
|
||||||
alert('Выберите действие')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Отправляем команду на бэкенд
|
|
||||||
const result = await sendCommandToPython(selectedAction.value)
|
|
||||||
|
|
||||||
console.log('Результат выполнения:', result)
|
|
||||||
|
|
||||||
if (result && result.status === 'success') {
|
|
||||||
alert(`Действие "${selectedAction.value}" выполнено успешно!`)
|
|
||||||
} else if (result && result.status === 'error') {
|
|
||||||
alert(`Ошибка при выполнении: ${result.error || 'Неизвестная ошибка'}`)
|
|
||||||
} else {
|
|
||||||
alert('Не удалось получить результат от сервера')
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Ошибка при выполнении действия:', err)
|
|
||||||
alert('Произошла ошибка при выполнении действия')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col gap-4 p-4">
|
<div class="p-4">
|
||||||
<h1>Компас — прослушка событий от Python</h1>
|
<h1>Компас — прослушка событий от Python</h1>
|
||||||
|
|
||||||
<!-- Статус -->
|
<div v-if="events.length === 0" class="text-gray-500 italic">
|
||||||
<div v-if="!isReady" class="mb-4 p-3 bg-yellow-100 text-yellow-800 rounded flex items-center">
|
Ожидание событий...
|
||||||
⚠️ <strong class="ml-2">Ещё рано отправлять запросы. Идёт инициализация...</strong>
|
|
||||||
</div>
|
|
||||||
<template v-else>
|
|
||||||
<div>
|
|
||||||
<!-- Кнопка синхронизации -->
|
|
||||||
<button @click="syncKompas"
|
|
||||||
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded mb-4 transition">
|
|
||||||
🔁 Синхронизировать КОМПАС
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Сообщение о пустом списке -->
|
<div v-else class="space-y-2 mt-4">
|
||||||
<div v-if="!documents.length" class="text-gray-500 italic mt-2">
|
<div v-for="(event, index) in events.slice().reverse()" :key="index" class="border p-3 rounded bg-gray-50">
|
||||||
Нет данных о документах. Нажмите "Синхронизировать КОМПАС".
|
<div class="text-sm text-gray-500">{{ event.timestamp.toLocaleTimeString() }}</div>
|
||||||
</div>
|
<strong>Тип события:</strong> {{ event.type }}
|
||||||
<template v-else>
|
<pre class="mt-1 text-xs bg-gray-100 p-2 rounded overflow-auto max-h-40">
|
||||||
<!-- Чекбоксы для фильтрации -->
|
{{ JSON.stringify(event.data, null, 2) }}
|
||||||
<div class="mb-4 p-3 bg-gray-100 rounded shadow-sm">
|
</pre>
|
||||||
<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>
|
</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" 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 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-1">
|
|
||||||
<strong>Активен:</strong>
|
|
||||||
<span class="ml-2">
|
|
||||||
{{ doc.active ? '✅' : '❌' }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mt-2 text-xs text-gray-500 truncate">
|
|
||||||
{{ doc.path }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
Loading…
Reference in New Issue