ваше сообщение коммита

This commit is contained in:
2025-09-02 17:18:15 +03:00
parent a6360ccd2e
commit 53bb269b85
26 changed files with 580 additions and 243 deletions

View File

@@ -65,7 +65,7 @@
</template>
<template v-else-if="column.type === 'multiselect-relation'">
<div v-if="!editing" @click="editing = true" class="tags-cell-view">
<span v-if="selectedMultiRelationNames.length">{{ selectedMultiRelationNames.map(prettyDisplay).join(', ') }}</span>
<span v-if="selectedMultiRelationNames.length">{{ selectedMultiRelationNames.join(', ') }}</span>
<span v-else class="cell-plus-icon" title="Добавить">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
<circle cx="9" cy="9" r="8" fill="#f3f4f6" stroke="#b6c6e6"/>
@@ -78,7 +78,7 @@
<div class="tags-multiselect">
<div v-for="option in multiRelationOptions" :key="option.id" class="tag-option">
<input type="checkbox" :id="'cell-multirel-' + option.id + '-' + rowId" :value="String(option.id)" v-model="editMultiRelationValues" />
<label :for="'cell-multirel-' + option.id + '-' + rowId">{{ prettyDisplay(option.display, multiRelationOptions.value) }}</label>
<label :for="'cell-multirel-' + option.id + '-' + rowId">{{ option.display }}</label>
<button class="delete-tag-btn" @click.prevent="deleteTag(option.id)" title="Удалить тег">×</button>
</div>
</div>
@@ -500,44 +500,55 @@ async function loadMultiRelationOptions() {
}
const rel = props.column.options || {};
if (rel.relatedTableId && rel.relatedColumnId) {
try {
// Проверяем кэш для данных таблицы
const cachedTableData = cacheService.getTableData(rel.relatedTableId, 'default');
let tableData;
if (cachedTableData) {
// console.log(`[loadMultiRelationOptions] ✅ Используем предварительно загруженные данные таблицы ${rel.relatedTableId}`);
tableData = cachedTableData;
} else {
// console.log(`[loadMultiRelationOptions] ⚠️ Данные таблицы ${rel.relatedTableId} не найдены в кэше, загружаем заново`);
const response = await fetch(`/api/tables/${rel.relatedTableId}`);
tableData = await response.json();
// Сохраняем в кэш
cacheService.setTableData(rel.relatedTableId, 'default', tableData);
}
// Формируем опции из данных таблицы
const colId = rel.relatedColumnId || (tableData.columns[0] && tableData.columns[0].id);
const opts = [];
for (const row of tableData.rows) {
const cell = tableData.cellValues.find(c => c.row_id === row.id && c.column_id === colId);
opts.push({ id: row.id, display: cell ? cell.value : `ID ${row.id}` });
}
multiRelationOptions.value = opts;
lastLoadedOptionsKey = cacheKey;
// Обновляем selectedMultiRelationNames на основе текущих значений
if (editMultiRelationValues.value.length > 0) {
selectedMultiRelationNames.value = opts
.filter(opt => editMultiRelationValues.value.includes(String(opt.id)))
.map(opt => opt.display);
} else {
selectedMultiRelationNames.value = [];
}
} catch (e) {
// console.error('[loadMultiRelationOptions] Error:', e);
// Проверяем, что options содержат необходимые данные
if (!rel.relatedTableId || !rel.relatedColumnId) {
console.warn('[loadMultiRelationOptions] Отсутствуют relatedTableId или relatedColumnId в options:', rel);
multiRelationOptions.value = [];
selectedMultiRelationNames.value = [];
return;
}
try {
// Проверяем кэш для данных таблицы
const cachedTableData = cacheService.getTableData(rel.relatedTableId, 'default');
let tableData;
if (cachedTableData) {
console.log(`[loadMultiRelationOptions] ✅ Используем предварительно загруженные данные таблицы ${rel.relatedTableId}`);
tableData = cachedTableData;
} else {
console.log(`[loadMultiRelationOptions] ⚠️ Данные таблицы ${rel.relatedTableId} не найдены в кэше, загружаем заново`);
const response = await fetch(`/api/tables/${rel.relatedTableId}`);
tableData = await response.json();
// Сохраняем в кэш
cacheService.setTableData(rel.relatedTableId, 'default', tableData);
}
// Формируем опции из данных таблицы
const colId = rel.relatedColumnId || (tableData.columns[0] && tableData.columns[0].id);
const opts = [];
for (const row of tableData.rows) {
const cell = tableData.cellValues.find(c => c.row_id === row.id && c.column_id === colId);
opts.push({ id: row.id, display: cell ? cell.value : `ID ${row.id}` });
}
multiRelationOptions.value = opts;
lastLoadedOptionsKey = cacheKey;
console.log('[loadMultiRelationOptions] Загружено опций:', opts.length);
// Обновляем selectedMultiRelationNames на основе текущих значений
if (editMultiRelationValues.value.length > 0) {
selectedMultiRelationNames.value = opts
.filter(opt => editMultiRelationValues.value.includes(String(opt.id)))
.map(opt => opt.display);
} else {
selectedMultiRelationNames.value = [];
}
} catch (e) {
console.error('[loadMultiRelationOptions] Error:', e);
multiRelationOptions.value = [];
selectedMultiRelationNames.value = [];
}
}

View File

@@ -26,8 +26,10 @@
<input id="dbPort" v-model.number="form.dbPort" type="number" required />
</div>
<div class="form-group">
<label for="dbName">Database</label>
<input id="dbName" v-model="form.dbName" type="text" required />
<label class="info-label">
<i class="info-icon"></i>
Database name: <strong>{{ form.dbName }}</strong> (неизменяемо)
</label>
</div>
<div class="form-group">
<label for="dbUser">User</label>
@@ -43,7 +45,7 @@
<div v-else class="settings-view">
<div class="view-row"><span>Host:</span> <b>{{ form.dbHost }}</b></div>
<div class="view-row"><span>Port:</span> <b>{{ form.dbPort }}</b></div>
<div class="view-row"><span>Database:</span> <b>{{ form.dbName }}</b></div>
<div class="view-row"><span>Database:</span> <b>{{ form.dbName }}</b> <span class="readonly-badge">(неизменяемо)</span></div>
<div class="view-row"><span>User:</span> <b>{{ form.dbUser }}</b></div>
<div class="view-row"><span>Password:</span> <b></b></div>
<button type="button" class="edit-btn" @click="editMode = true">Изменить</button>
@@ -97,12 +99,13 @@ onMounted(async () => {
const saveDbSettings = async () => {
try {
// Отправляем только безопасные для изменения поля
await api.put('/settings/db-settings', {
db_host: form.dbHost,
db_port: form.dbPort,
db_name: form.dbName,
db_user: form.dbUser,
db_password: form.dbPassword || undefined
// db_name не отправляем - он неизменяем
});
alert('Настройки базы данных сохранены');
form.dbPassword = '';
@@ -219,4 +222,32 @@ h2 {
.edit-btn:hover {
background: var(--color-primary-dark);
}
.empty-placeholder {
color: #888;
font-size: 1em;
margin: 0.7em 0;
}
.info-label {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 6px;
padding: 0.75rem;
color: #495057;
font-size: 0.95em;
}
.info-icon {
margin-right: 0.5rem;
color: #007bff;
}
.readonly-badge {
background: #6c757d;
color: white;
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-size: 0.8em;
margin-left: 0.5rem;
}
</style>