up...
This commit is contained in:
@@ -95,19 +95,19 @@
|
||||
|
||||
<router-link
|
||||
v-if="userStore.role === 'admin'"
|
||||
to="/olap-constructor"
|
||||
to="/olap/queries"
|
||||
class="flex items-center rounded-lg hover:bg-gray-100 transition-colors group"
|
||||
:class="[
|
||||
route.path === '/olap-constructor' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
||||
route.path === '/olap/queries' ? 'bg-primary-50 text-primary-700' : 'text-gray-700',
|
||||
sidebarCollapsed ? 'justify-center p-2' : 'px-4 py-3 space-x-3'
|
||||
]"
|
||||
:title="sidebarCollapsed ? 'OLAP Конструктор' : ''"
|
||||
:title="sidebarCollapsed ? 'OLAP Queries' : ''"
|
||||
>
|
||||
<svg class="w-5 h-5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V8a2 2 0 012-2z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 6v12M16 6v12" />
|
||||
</svg>
|
||||
<span v-if="!sidebarCollapsed" class="truncate">OLAP Конструктор</span>
|
||||
<span v-if="!sidebarCollapsed" class="truncate">OLAP Queries</span>
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
|
||||
@@ -11,7 +11,9 @@ import OlapColumnsView from '@/views/OlapColumnsView.vue'
|
||||
import DBConnections from '@/views/DBConnections.vue'
|
||||
import AdminSettings from '@/views/AdminSettings.vue'
|
||||
import Profile from '@/views/Profile.vue'
|
||||
import OLAPConstructor from '@/views/OLAPConstructor.vue'
|
||||
import OlapQueriesPage from '@/views/OlapQueriesPage.vue'
|
||||
import OlapConstructor from '@/views/OlapConstructor.vue'
|
||||
|
||||
import NotFound from '@/views/NotFound.vue'
|
||||
|
||||
const routes = [
|
||||
@@ -64,9 +66,19 @@ const routes = [
|
||||
component: AdminSettings,
|
||||
meta: { requiresAuth: true, requiresAdmin: true, title: 'Settings' }
|
||||
},
|
||||
// {
|
||||
// path: '/olap-constructor',
|
||||
// component: OLAPConstructor,
|
||||
// meta: { requiresAuth: true, title: 'OLAP Constructor' }
|
||||
// },
|
||||
{
|
||||
path: '/olap-constructor',
|
||||
component: OLAPConstructor,
|
||||
path: '/olap/queries',
|
||||
component: OlapQueriesPage,
|
||||
meta: { requiresAuth: true, title: 'OLAP Queries' }
|
||||
},
|
||||
{
|
||||
path: '/olap/constructor/:id?',
|
||||
component: OlapConstructor,
|
||||
meta: { requiresAuth: true, title: 'OLAP Constructor' }
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
131
frontend/src/views/OlapQueriesPage.vue
Normal file
131
frontend/src/views/OlapQueriesPage.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<template>
|
||||
<AppLayout>
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<h1 class="text-2xl font-bold text-gray-900">OLAP запросы</h1>
|
||||
<router-link to="/olap/constructor" class="btn-primary">+ Создать запрос</router-link>
|
||||
</div>
|
||||
|
||||
<div class="card overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full divide-y divide-gray-200">
|
||||
<thead class="bg-gray-50">
|
||||
<tr>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500">ID</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500">Название</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500">Подключение</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500">Рестораны</th>
|
||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500">Создан</th>
|
||||
<th class="px-6 py-3 text-right text-xs font-medium text-gray-500">Действия</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-200 bg-white">
|
||||
<tr v-for="q in queries" :key="q.id" class="hover:bg-gray-50">
|
||||
<td class="px-6 py-4 text-sm">{{ q.id }}</td>
|
||||
<td class="px-6 py-4 text-sm font-medium">{{ q.name }}</td>
|
||||
<td class="px-6 py-4 text-sm">{{ q.dbConnectionName }}</td>
|
||||
<td class="px-6 py-4 text-sm">{{ q.restaurants }}</td>
|
||||
<td class="px-6 py-4 text-sm">{{ formatDate(q.created) }}</td>
|
||||
<td class="px-6 py-4 text-right space-x-2">
|
||||
<router-link :to="`/olap/constructor/${q.id}`" class="text-blue-600 hover:text-blue-800">
|
||||
<svg class="w-5 h-5 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
</svg>
|
||||
</router-link>
|
||||
<button @click="confirmDelete(q.id)" class="text-red-600 hover:text-red-800">
|
||||
<svg class="w-5 h-5 inline" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr v-if="queries.length === 0">
|
||||
<td colspan="6" class="px-6 py-12 text-center text-gray-500">Нет запросов. Создайте первый!</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Модальное окно удаления с улучшенной стилизацией -->
|
||||
<Teleport to="body">
|
||||
<Transition name="fade">
|
||||
<div v-if="deleteModal.show" class="fixed inset-0 z-[9999] overflow-y-auto" @click.self="deleteModal.show = false">
|
||||
<div class="fixed inset-0 bg-black/50 backdrop-blur-sm"></div>
|
||||
<div class="flex items-center justify-center min-h-screen p-4">
|
||||
<div class="relative bg-white rounded-2xl shadow-xl max-w-md w-full transform transition-all">
|
||||
<div class="p-6 text-center">
|
||||
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4">
|
||||
<svg class="h-6 w-6 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-lg font-medium text-gray-900 mb-2">Удалить запрос?</h3>
|
||||
<p class="text-sm text-gray-500 mb-6">Действие необратимо. Вы уверены?</p>
|
||||
<div class="flex justify-center space-x-3">
|
||||
<button @click="deleteModal.show = false" class="btn-secondary">Отмена</button>
|
||||
<button @click="deleteQuery" class="bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700 transition-colors">Удалить</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import AppLayout from '@/components/Layout/AppLayout.vue'
|
||||
import { useNotification } from '@/composables/useNotification'
|
||||
|
||||
const { showNotification } = useNotification()
|
||||
const queries = ref([])
|
||||
const deleteModal = ref({ show: false, id: null as number | null })
|
||||
|
||||
async function loadQueries() {
|
||||
try {
|
||||
const res = await fetch('/api/olap/queries')
|
||||
if (!res.ok) throw new Error()
|
||||
queries.value = await res.json()
|
||||
} catch (e) {
|
||||
showNotification('Ошибка загрузки запросов', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
function formatDate(dateStr: string | null) {
|
||||
return dateStr ? new Date(dateStr).toLocaleString() : '-'
|
||||
}
|
||||
|
||||
function confirmDelete(id: number) {
|
||||
deleteModal.value = { show: true, id }
|
||||
}
|
||||
|
||||
async function deleteQuery() {
|
||||
const id = deleteModal.value.id
|
||||
if (!id) return
|
||||
try {
|
||||
const res = await fetch(`/api/olap/queries/${id}`, { method: 'DELETE' })
|
||||
if (!res.ok) throw new Error()
|
||||
showNotification('Запрос удалён', 'success')
|
||||
await loadQueries()
|
||||
} catch (e) {
|
||||
showNotification('Ошибка удаления', 'error')
|
||||
} finally {
|
||||
deleteModal.value.show = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadQueries)
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user