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

This commit is contained in:
2025-09-24 13:05:20 +03:00
parent de0f8aecf2
commit 76cde4b53d
45 changed files with 2167 additions and 2854 deletions

View File

@@ -13,10 +13,10 @@
<template>
<div class="contact-table-modal">
<div class="contact-table-header">
<el-button type="info" :disabled="!selectedIds.length" @click="showBroadcastModal = true" style="margin-right: 1em;">Рассылка</el-button>
<el-button type="warning" :disabled="!selectedIds.length" @click="deleteMessagesSelected" style="margin-right: 1em;">Удалить сообщения</el-button>
<el-button type="danger" :disabled="!selectedIds.length" @click="deleteSelected" style="margin-right: 1em;">Удалить</el-button>
<el-button type="primary" @click="showImportModal = true" style="margin-right: 1em;">Импорт</el-button>
<el-button v-if="canManageSettings" type="info" :disabled="!selectedIds.length" @click="showBroadcastModal = true" style="margin-right: 1em;">Рассылка</el-button>
<el-button v-if="canDelete" type="warning" :disabled="!selectedIds.length" @click="deleteMessagesSelected" style="margin-right: 1em;">Удалить сообщения</el-button>
<el-button v-if="canDelete" type="danger" :disabled="!selectedIds.length" @click="deleteSelected" style="margin-right: 1em;">Удалить</el-button>
<el-button v-if="canEdit" type="primary" @click="showImportModal = true" style="margin-right: 1em;">Импорт</el-button>
<button class="close-btn" @click="$emit('close')">×</button>
</div>
<el-form :inline="true" class="filters-form" label-position="top">
@@ -74,7 +74,7 @@
<table class="contact-table">
<thead>
<tr>
<th><input type="checkbox" v-model="selectAll" @change="toggleSelectAll" /></th>
<th v-if="canEdit || canDelete || canManageSettings"><input type="checkbox" v-model="selectAll" @change="toggleSelectAll" /></th>
<th>Имя</th>
<th>Email</th>
<th>Telegram</th>
@@ -85,7 +85,7 @@
</thead>
<tbody>
<tr v-for="contact in contactsArray" :key="contact.id" :class="{ 'new-contact-row': newIds.includes(contact.id) }">
<td><input type="checkbox" v-model="selectedIds" :value="contact.id" /></td>
<td v-if="canEdit || canDelete || canManageSettings"><input type="checkbox" v-model="selectedIds" :value="contact.id" /></td>
<td>{{ contact.name || '-' }}</td>
<td>{{ contact.email || '-' }}</td>
<td>{{ contact.telegram || '-' }}</td>
@@ -112,6 +112,7 @@ import BroadcastModal from './BroadcastModal.vue';
import tablesService from '../services/tablesService';
import messagesService from '../services/messagesService';
import { useTagsWebSocket } from '../composables/useTagsWebSocket';
import { usePermissions } from '@/composables/usePermissions';
const props = defineProps({
contacts: { type: Array, default: () => [] },
newContacts: { type: Array, default: () => [] },
@@ -123,6 +124,7 @@ const contactsArray = ref([]); // теперь управляем вручную
const newIds = computed(() => props.newContacts.map(c => c.id));
const newMsgUserIds = computed(() => props.newMessages.map(m => String(m.user_id)));
const router = useRouter();
const { canEdit, canDelete, canManageSettings } = usePermissions();
// Фильтры
const filterSearch = ref('');

View File

@@ -12,7 +12,7 @@
<template>
<template v-if="column.type === 'multiselect'">
<div v-if="!editing" @click="editing = true" class="tags-cell-view">
<div v-if="!editing" @click="canEdit && (editing = true)" class="tags-cell-view">
<span v-if="selectedMultiNames.length">{{ selectedMultiNames.join(', ') }}</span>
<span v-else class="cell-plus-icon" title="Добавить">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
@@ -39,7 +39,7 @@
</div>
</template>
<template v-else-if="column.type === 'relation'">
<div v-if="!editing" @click="editing = true" class="tags-cell-view">
<div v-if="!editing" @click="canEdit && (editing = true)" class="tags-cell-view">
<span v-if="selectedRelationName">{{ selectedRelationName }}</span>
<span v-else class="cell-plus-icon" title="Добавить">
<svg width="18" height="18" viewBox="0 0 18 18" fill="none">
@@ -64,7 +64,7 @@
</div>
</template>
<template v-else-if="column.type === 'multiselect-relation'">
<div v-if="!editing" @click="editing = true" class="tags-cell-view">
<div v-if="!editing" @click="canEdit && (editing = true)" class="tags-cell-view">
<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">
@@ -97,7 +97,7 @@
</div>
</template>
<template v-else>
<div v-if="!editing" class="cell-view-value" @click="editing = true">
<div v-if="!editing" class="cell-view-value" @click="canEdit && (editing = true)">
<span v-if="isArrayString(localValue)">{{ parseArrayString(localValue).join(', ') }}</span>
<span v-else-if="localValue">{{ localValue }}</span>
<span v-else class="cell-plus-icon" title="Добавить">
@@ -128,8 +128,11 @@ import tablesService from '../../services/tablesService';
import { useTablesWebSocket } from '../../composables/useTablesWebSocket';
import { useTagsWebSocket } from '../../composables/useTagsWebSocket';
import cacheService from '../../services/cacheService';
import { usePermissions } from '@/composables/usePermissions';
const props = defineProps(['rowId', 'column', 'cellValues']);
const emit = defineEmits(['update']);
const { canEdit } = usePermissions();
const localValue = ref('');
const editing = ref(false);

View File

@@ -15,9 +15,9 @@
<h2>{{ tableMeta.name }}</h2>
<div class="table-desc">{{ tableMeta.description }}</div>
<div class="table-header-actions" style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 8px; margin-bottom: 18px;">
<el-button type="danger" :disabled="!selectedRows.length" @click="deleteSelectedRows">Удалить выбранные</el-button>
<el-button v-if="canEdit" type="danger" :disabled="!selectedRows.length" @click="deleteSelectedRows">Удалить выбранные</el-button>
<span v-if="selectedRows.length">Выбрано: {{ selectedRows.length }}</span>
<button v-if="isAdmin" class="rebuild-btn" @click="rebuildIndex" :disabled="rebuilding">
<button v-if="canEdit" class="rebuild-btn" @click="rebuildIndex" :disabled="rebuilding">
{{ rebuilding ? 'Пересборка...' : 'Пересобрать индекс' }}
</button>
<el-button @click="resetFilters" type="default" icon="el-icon-refresh">Сбросить фильтры</el-button>
@@ -68,7 +68,7 @@
</template>
<template v-else>
<span>{{ col.name }}</span>
<button class="col-menu" @click.stop="openColMenu(col, $event)">⋮</button>
<button v-if="canEdit" class="col-menu" @click.stop="openColMenu(col, $event)">⋮</button>
</template>
</template>
<template #default="{ row }">
@@ -90,7 +90,7 @@
:resizable="false"
>
<template #header>
<button class="add-col-btn" @click.stop="openAddMenu($event)" title="Добавить">
<button v-if="canEdit" class="add-col-btn" @click.stop="openAddMenu($event)" title="Добавить">
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="11" cy="11" r="10" fill="#f3f4f6" stroke="#b6c6e6"/>
<rect x="10" y="5.5" width="2" height="11" rx="1" fill="#4f8cff"/>
@@ -105,7 +105,7 @@
</teleport>
</template>
<template #default="{ row }">
<button class="row-menu" @click.stop="openRowMenu(row, $event)"></button>
<button v-if="canEdit" class="row-menu" @click.stop="openRowMenu(row, $event)"></button>
<teleport to="body">
<div v-if="openedRowMenuId === row.id" class="context-menu" :style="rowMenuStyle">
<button class="menu-item" @click="addRowAfter(row)">Добавить строку</button>
@@ -170,6 +170,7 @@ import { ref, onMounted, computed, watch, onUnmounted } from 'vue';
import tablesService from '../../services/tablesService';
import TableCell from './TableCell.vue';
import { useAuthContext } from '@/composables/useAuth';
import { usePermissions } from '@/composables/usePermissions';
import axios from 'axios';
// Импортируем компоненты Element Plus
import { ElSelect, ElOption, ElButton } from 'element-plus';
@@ -180,6 +181,7 @@ let unsubscribeFromTableUpdate = null;
let unsubscribeFromTagsUpdate = null;
const { isAdmin } = useAuthContext();
const { canEdit } = usePermissions();
const rebuilding = ref(false);
const rebuildStatus = ref(null);