feat: новая функция
This commit is contained in:
@@ -156,7 +156,17 @@
|
||||
</div>
|
||||
<label>Плейсхолдер</label>
|
||||
<input v-model="newColPlaceholder" class="notion-input" placeholder="Плейсхолдер (авто)" />
|
||||
<!-- Удаляю блок назначения столбца -->
|
||||
<label>Назначение (для RAG)</label>
|
||||
<select v-model="newColPurpose" class="notion-input">
|
||||
<option value="">Без назначения</option>
|
||||
<option value="question">Вопрос для AI</option>
|
||||
<option value="answer">Ответ AI</option>
|
||||
<option value="product">Продукт</option>
|
||||
<option value="userTags">Теги пользователя</option>
|
||||
<option value="context">Контекст</option>
|
||||
<option value="priority">Приоритет</option>
|
||||
<option value="date">Дата</option>
|
||||
</select>
|
||||
<div class="modal-actions">
|
||||
<button class="save-btn" @click="handleAddColumn">Добавить</button>
|
||||
<button class="cancel-btn" @click="closeAddColModal">Отмена</button>
|
||||
@@ -184,7 +194,7 @@ onMounted(() => {
|
||||
|
||||
window.addEventListener('refresh-application-data', () => {
|
||||
console.log('[UserTableView] Refreshing table data');
|
||||
loadTableData(); // Обновляем данные при входе в систему
|
||||
fetchTable(); // Обновляем данные при входе в систему
|
||||
});
|
||||
});
|
||||
// Импортируем компоненты Element Plus
|
||||
@@ -236,6 +246,7 @@ const relatedColumnId = ref(null);
|
||||
const relatedTableColumns = ref([]);
|
||||
const newColPlaceholder = ref('');
|
||||
const multiOptionsInput = ref('');
|
||||
const newColPurpose = ref('');
|
||||
|
||||
// Новые фильтры по relation/multiselect/lookup
|
||||
const relationFilters = ref({});
|
||||
@@ -304,6 +315,7 @@ function closeAddColModal() {
|
||||
selectedTagIds.value = [];
|
||||
newColPlaceholder.value = '';
|
||||
multiOptionsInput.value = '';
|
||||
newColPurpose.value = '';
|
||||
}
|
||||
|
||||
async function handleAddColumn() {
|
||||
@@ -325,6 +337,9 @@ async function handleAddColumn() {
|
||||
options.relatedTableId = relatedTableId.value;
|
||||
options.relatedColumnId = relatedColumnId.value;
|
||||
}
|
||||
if (newColPurpose.value) {
|
||||
options.purpose = newColPurpose.value;
|
||||
}
|
||||
if (Object.keys(options).length > 0) {
|
||||
data.options = options;
|
||||
}
|
||||
@@ -902,9 +917,6 @@ async function updateRowData(rowId) {
|
||||
min-width: 80px;
|
||||
max-width: 600px;
|
||||
}
|
||||
.el-table-row-custom {
|
||||
/* Можно добавить стили для высоты строк, если нужно */
|
||||
}
|
||||
|
||||
.notion-input {
|
||||
width: 100%;
|
||||
|
||||
@@ -562,9 +562,6 @@ export function useChat(auth) {
|
||||
console.log('[useChat] Clearing chat data');
|
||||
// Очищаем данные при выходе из системы
|
||||
messages.value = [];
|
||||
newMessages.value = [];
|
||||
readUserIds.value = [];
|
||||
lastReadMessageDate.value = {};
|
||||
});
|
||||
|
||||
window.addEventListener('refresh-application-data', () => {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
:messages="messages"
|
||||
:is-loading="isLoading || isConnectingWallet"
|
||||
:has-more-messages="messageLoading.hasMoreMessages"
|
||||
:currentUserId="auth.userId"
|
||||
:currentUserId="auth.userId.value"
|
||||
v-model:newMessage="newMessage"
|
||||
v-model:attachments="attachments"
|
||||
@send-message="handleSendMessage"
|
||||
@@ -45,7 +45,7 @@
|
||||
:messages="messages"
|
||||
:is-loading="isLoading || isConnectingWallet"
|
||||
:has-more-messages="messageLoading.hasMoreMessages"
|
||||
:currentUserId="auth.userId"
|
||||
:currentUserId="auth.userId.value"
|
||||
v-model:newMessage="newMessage"
|
||||
v-model:attachments="attachments"
|
||||
@send-message="handleSendMessage"
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<!-- Основной контент -->
|
||||
<div class="page-content">
|
||||
<h2>Содержание</h2>
|
||||
<div class="content-text" v-if="page.format === 'html'" v-html="formatContent(page.content)"></div>
|
||||
<div class="content-text" v-if="page.format === 'html'" v-html="formatContent"></div>
|
||||
<div v-else-if="page.format === 'pdf' && page.file_path" class="file-preview">
|
||||
<embed :src="page.file_path" type="application/pdf" class="pdf-embed" />
|
||||
<a class="btn btn-outline" :href="page.file_path" target="_blank" download>Скачать PDF</a>
|
||||
@@ -106,10 +106,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import BaseLayout from '../../components/BaseLayout.vue';
|
||||
import pagesService from '../../services/pagesService';
|
||||
import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
// Props
|
||||
const props = defineProps({
|
||||
@@ -161,10 +163,26 @@ function formatAddress(address) {
|
||||
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
||||
}
|
||||
|
||||
function formatContent(content) {
|
||||
const formatContent = computed(() => {
|
||||
if (!page.value || !page.value.content) return '';
|
||||
const content = page.value.content;
|
||||
|
||||
// Проверяем, является ли контент markdown (содержит markdown синтаксис)
|
||||
const isMarkdown = /^#{1,6}\s|^\*\s|^\-\s|^\d+\.\s|```|\[.+\]\(.+\)|!\[.+\]\(.+\)/m.test(content);
|
||||
|
||||
if (isMarkdown) {
|
||||
// Конвертируем markdown в HTML
|
||||
const rawHtml = marked.parse(content);
|
||||
return DOMPurify.sanitize(rawHtml);
|
||||
} else {
|
||||
// Простое форматирование - замена переносов строк на <br>
|
||||
return content.replace(/\n/g, '<br>');
|
||||
}
|
||||
});
|
||||
|
||||
function formatContentAsFunc(content) {
|
||||
if (!content) return '';
|
||||
if (typeof content !== 'string') return '';
|
||||
// Простое форматирование - замена переносов строк на <br>
|
||||
return content.replace(/\n/g, '<br>');
|
||||
}
|
||||
|
||||
@@ -284,6 +302,119 @@ onMounted(() => {
|
||||
font-size: 1rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
/* Markdown стили */
|
||||
.content-text h1 {
|
||||
color: var(--color-primary);
|
||||
font-size: 2rem;
|
||||
margin: 1.5rem 0 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-text h2 {
|
||||
color: var(--color-primary);
|
||||
font-size: 1.5rem;
|
||||
margin: 1.25rem 0 0.75rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-text h3 {
|
||||
color: var(--color-primary);
|
||||
font-size: 1.25rem;
|
||||
margin: 1rem 0 0.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-text h4 {
|
||||
color: var(--color-primary);
|
||||
font-size: 1.1rem;
|
||||
margin: 0.75rem 0 0.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-text p {
|
||||
margin: 0.75rem 0;
|
||||
}
|
||||
|
||||
.content-text ul,
|
||||
.content-text ol {
|
||||
margin: 1rem 0;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.content-text li {
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.content-text code {
|
||||
background: #f4f4f4;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.content-text pre {
|
||||
background: #f4f4f4;
|
||||
padding: 1rem;
|
||||
border-radius: var(--radius-sm);
|
||||
overflow-x: auto;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.content-text pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.content-text blockquote {
|
||||
border-left: 4px solid var(--color-primary);
|
||||
padding-left: 1rem;
|
||||
margin: 1rem 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.content-text table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
.content-text table th,
|
||||
.content-text table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 0.5rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.content-text table th {
|
||||
background: #f8f9fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.content-text a {
|
||||
color: var(--color-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.content-text a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.content-text hr {
|
||||
border: none;
|
||||
border-top: 2px solid #f0f0f0;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.content-text strong {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.content-text em {
|
||||
font-style: italic;
|
||||
}
|
||||
.seo-info { display: grid; gap: 12px; }
|
||||
.seo-item { display: flex; justify-content: space-between; align-items: flex-start; padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
|
||||
.seo-item:last-child { border-bottom: none; }
|
||||
|
||||
@@ -85,7 +85,7 @@ const canManageLegalDocs = computed(() => hasPermission(SHARED_PERMISSIONS.MANAG
|
||||
|
||||
function goBack() { router.push({ name: 'content-list' }); }
|
||||
function openPublic(id) { router.push({ name: 'public-page-view', params: { id } }); }
|
||||
function goEdit(id) { router.push({ name: 'page-edit', params: { id } }); }
|
||||
function goEdit(id) { router.push({ name: 'content-create', query: { edit: id } }); }
|
||||
async function reindex(id) {
|
||||
try {
|
||||
await api.post(`/pages/${id}/reindex`);
|
||||
|
||||
@@ -300,7 +300,17 @@ async function loadEmbeddingModels() {
|
||||
}
|
||||
async function loadPlaceholders() {
|
||||
const { data } = await axios.get('/tables/placeholders/all');
|
||||
placeholders.value = Array.isArray(data) ? data : [];
|
||||
const allPlaceholders = Array.isArray(data) ? data : [];
|
||||
|
||||
// Фильтруем только плейсхолдеры из выбранных RAG таблиц
|
||||
if (settings.value.selected_rag_tables) {
|
||||
const selectedTableId = typeof settings.value.selected_rag_tables === 'object'
|
||||
? settings.value.selected_rag_tables[0]
|
||||
: settings.value.selected_rag_tables;
|
||||
placeholders.value = allPlaceholders.filter(ph => ph.table_id === Number(selectedTableId));
|
||||
} else {
|
||||
placeholders.value = [];
|
||||
}
|
||||
}
|
||||
function openEditPlaceholder(ph) {
|
||||
editingPlaceholder.value = { ...ph };
|
||||
@@ -316,6 +326,11 @@ async function savePlaceholderEdit() {
|
||||
await loadPlaceholders();
|
||||
closeEditPlaceholder();
|
||||
}
|
||||
// Обновляем плейсхолдеры при изменении выбранной RAG таблицы
|
||||
watch(() => settings.value.selected_rag_tables, () => {
|
||||
loadPlaceholders();
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
await loadSettings();
|
||||
await loadUserTables();
|
||||
|
||||
Reference in New Issue
Block a user