This commit is contained in:
2026-05-07 17:36:03 +03:00
parent 096fb1a3e2
commit 59e283945c
5 changed files with 126 additions and 49 deletions

View File

@@ -66,11 +66,6 @@ const routes = [
component: AdminSettings,
meta: { requiresAuth: true, requiresAdmin: true, title: 'Settings' }
},
// {
// path: '/olap-constructor',
// component: OLAPConstructor,
// meta: { requiresAuth: true, title: 'OLAP Constructor' }
// },
{
path: '/olap/queries',
component: OlapQueriesPage,

View File

@@ -135,7 +135,7 @@
<!-- Таблица SQL -->
<div class="flex flex-col">
<label class="text-xs text-gray-500">Таблица SQL</label>
<label class="text-xs text-gray-500">Таблица SQL *</label>
<input type="text" v-model="tableName" @input="validateTableName" class="input-field py-1 text-sm"
:class="{ 'border-green-500 bg-green-50': tableNameValid && tableName, 'border-red-500 bg-red-50': tableNameTouched && !tableNameValid && tableName }" />
</div>
@@ -581,7 +581,7 @@ const daysBack = ref(7)
const tableName = ref('')
const tableNameValid = ref(true)
const tableNameTouched = ref(false)
const active = ref(true)
const active = ref(false)
const searchQuery = ref('')
const activeTab = ref<'table' | 'sql'>('table')
const collapsed = ref({ number: false, category: false, filter: false })
@@ -874,6 +874,8 @@ async function loadQuery(id: number) {
// 1. Базовые поля запроса
queryName.value = data.name
active.value = data.active ?? true;
// 2. Подключение к БД
if (data.dbConnectionId) {
await loadDbConnections()
@@ -974,13 +976,18 @@ async function saveQuery() {
showNotification('Выберите хотя бы один ресторан', 'error')
return
}
if (!tableName.value.trim()) {
showNotification('Укажите название таблицы SQL', 'error');
return;
}
const config = buildConfigObject()
const payload = {
name: queryName.value,
dbConnectionId: selectedDbConnection.value.id,
config: config,
restaurantIds: selectedRestaurants.value.map(r => r.id)
}
restaurantIds: selectedRestaurants.value.map(r => r.id),
active: active.value
};
const url = queryId.value ? `/api/olap/queries/${queryId.value}` : '/api/olap/queries'
const method = queryId.value ? 'PUT' : 'POST'
try {
@@ -1011,9 +1018,17 @@ const filteredCategoryFields = computed(() => availableFields.value.filter((f):
const filteredFilterFields = computed(() => availableFields.value.filter((f): f is FilterField => f.role === 'filter' && matchesSearch(f)))
const validateTableName = () => {
tableNameTouched.value = true
if (!tableName.value) { tableNameValid.value = true; return }
tableNameValid.value = /^[A-Za-zА-Яа-я]/.test(tableName.value)
tableNameTouched.value = true;
const value = tableName.value.trim();
if (!value) {
tableNameValid.value = false;
return;
}
// Регулярное выражение:
// ^[A-Za-z] - первая буква (только английская)
// [A-Za-z0-9]* - далее любые английские буквы или цифры
const regex = /^[A-Za-z][A-Za-z0-9]*$/;
tableNameValid.value = regex.test(value);
}
const parseValues = (f: FilterField) => {
@@ -1184,7 +1199,7 @@ const confirmReset = () => {
refreshFieldsAndReset()
dateTo.value = ''
daysBack.value = 7
active.value = true
active.value = false
tableName.value = ''
tableNameValid.value = true
tableNameTouched.value = false

View File

@@ -12,6 +12,9 @@
<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-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>
@@ -22,6 +25,17 @@
<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">
<span :class="q.active ? 'text-green-600' : 'text-red-600'">
{{ q.active ? 'Да' : 'Нет' }}
</span>
</td>
<td class="px-6 py-4 text-sm">{{ q.lastRun ? formatDate(q.lastRun) : '—' }}</td>
<td class="px-6 py-4 text-sm">
<span v-if="q.lastRunSuccess === null"></span>
<span v-else-if="q.lastRunSuccess" class="text-green-600">Успешно</span>
<span v-else class="text-red-600">Ошибка</span>
</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>