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

This commit is contained in:
2025-06-04 14:39:36 +03:00
parent 2c2115869c
commit 4d5cef853e
17 changed files with 1046 additions and 2038 deletions

View File

@@ -15,6 +15,12 @@
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="message.content" class="message-content" v-html="formattedContent" />
<!-- Кнопки для системного сообщения -->
<div v-if="message.sender_type === 'system' && (message.telegramBotUrl || message.supportEmail)" class="system-actions">
<button v-if="message.telegramBotUrl" @click="openTelegram(message.telegramBotUrl)" class="system-btn">Перейти в Telegram-бот</button>
<button v-if="message.supportEmail" @click="copyEmail(message.supportEmail)" class="system-btn">Скопировать email</button>
</div>
<!-- Блок для отображения прикрепленного файла (теперь с плеерами/изображением/ссылкой) -->
<div v-if="attachment" class="message-attachments">
<div class="attachment-item">
@@ -168,6 +174,14 @@ const formatFileSize = (bytes) => {
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
function openTelegram(url) {
window.open(url, '_blank');
}
function copyEmail(email) {
navigator.clipboard.writeText(email);
// Можно добавить уведомление "Email скопирован"
}
</script>
<style scoped>
@@ -360,4 +374,23 @@ const formatFileSize = (bytes) => {
max-height: 200px;
}
}
.system-actions {
margin-top: 10px;
display: flex;
gap: 10px;
}
.system-btn {
background: var(--color-primary, #3b82f6);
color: #fff;
border: none;
border-radius: 6px;
padding: 6px 14px;
cursor: pointer;
font-size: 1em;
transition: background 0.2s;
}
.system-btn:hover {
background: var(--color-primary-dark, #2563eb);
}
</style>

View File

@@ -0,0 +1,122 @@
<template>
<div class='modal-bg'>
<div class='modal'>
<h3>{{ rule ? 'Редактировать' : 'Создать' }} набор правил</h3>
<label>Название</label>
<input v-model="name" />
<label>Описание</label>
<textarea v-model="description" rows="3" placeholder="Опишите правило в свободной форме" />
<button type="button" @click="convertToJson" style="margin: 0.5rem 0;">Преобразовать в JSON</button>
<label>Правила (JSON)</label>
<textarea v-model="rulesJson" rows="6"></textarea>
<div v-if="error" class="error">{{ error }}</div>
<div class="actions">
<button @click="save">Сохранить</button>
<button @click="close">Отмена</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import axios from 'axios';
const emit = defineEmits(['close']);
const props = defineProps({ rule: Object });
const name = ref(props.rule ? props.rule.name : '');
const description = ref(props.rule ? props.rule.description : '');
const rulesJson = ref(props.rule ? JSON.stringify(props.rule.rules, null, 2) : '{\n "checkUserTags": true\n}');
const error = ref('');
watch(() => props.rule, (newRule) => {
name.value = newRule ? newRule.name : '';
description.value = newRule ? newRule.description : '';
rulesJson.value = newRule ? JSON.stringify(newRule.rules, null, 2) : '{\n "checkUserTags": true\n}';
});
function convertToJson() {
// Простейший пример: если в описании есть "теги", выставляем checkUserTags
// В реальном проекте здесь можно интегрировать LLM или шаблоны
try {
if (/тег[а-я]* пользов/.test(description.value.toLowerCase())) {
rulesJson.value = JSON.stringify({ checkUserTags: true }, null, 2);
error.value = '';
} else {
rulesJson.value = JSON.stringify({ customRule: description.value }, null, 2);
error.value = '';
}
} catch (e) {
error.value = 'Не удалось преобразовать описание в JSON';
}
}
async function save() {
let rules;
try {
rules = JSON.parse(rulesJson.value);
} catch (e) {
error.value = 'Ошибка в формате JSON!';
return;
}
if (props.rule && props.rule.id) {
await axios.put(`/api/settings/ai-assistant-rules/${props.rule.id}`, { name: name.value, description: description.value, rules });
} else {
await axios.post('/api/settings/ai-assistant-rules', { name: name.value, description: description.value, rules });
}
emit('close', true);
}
function close() { emit('close', false); }
</script>
<style scoped>
.modal-bg {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.25);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal {
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 16px rgba(0,0,0,0.12);
padding: 2rem;
min-width: 320px;
max-width: 420px;
}
label {
display: block;
margin-top: 1rem;
font-weight: 500;
}
input, textarea {
width: 100%;
margin-top: 0.5rem;
padding: 0.5rem;
border-radius: 6px;
border: 1px solid #ddd;
font-size: 1rem;
}
.actions {
display: flex;
gap: 1rem;
margin-top: 2rem;
}
button {
background: var(--color-primary);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
}
button:last-child {
background: #eee;
color: #333;
}
.error {
color: #c00;
margin-top: 0.5rem;
}
</style>