feat: новая функция
This commit is contained in:
@@ -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