modified: serv_nginx/bbvue/src/views/News.vue
add checkAuth in onMounted
This commit is contained in:
@@ -32,8 +32,7 @@
|
||||
<div class="news-controls">
|
||||
<div class="news-filters">
|
||||
<button v-for="filter in filters" :key="filter.value"
|
||||
:class="['filter-btn', { 'active': activeFilter === filter.value }]"
|
||||
@click="setFilter(filter.value)">
|
||||
:class="['filter-btn', { 'active': activeFilter === filter.value }]" @click="setFilter(filter.value)">
|
||||
{{ filter.label }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -162,13 +161,12 @@
|
||||
<!-- Секция комментариев -->
|
||||
<div class="news-comments">
|
||||
<h4>💬 Комментарии ({{ comments.length }})</h4>
|
||||
|
||||
|
||||
<!-- Форма добавления комментария -->
|
||||
<div v-if="isAuthenticated" class="comment-form">
|
||||
<textarea v-model="newComment" placeholder="Напишите ваш комментарий..."
|
||||
class="comment-input" rows="3"></textarea>
|
||||
<button class="btn btn-primary" @click="addComment"
|
||||
:disabled="!newComment.trim() || commentLoading">
|
||||
<textarea v-model="newComment" placeholder="Напишите ваш комментарий..." class="comment-input"
|
||||
rows="3"></textarea>
|
||||
<button class="btn btn-primary" @click="addComment" :disabled="!newComment.trim() || commentLoading">
|
||||
{{ commentLoading ? 'Отправка...' : '💬 Отправить комментарий' }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -190,9 +188,8 @@
|
||||
<span class="author-name">{{ comment.author.first_name }} {{ comment.author.last_name }}</span>
|
||||
<span class="comment-date">{{ formatDate(comment.created_at) }}</span>
|
||||
</div>
|
||||
<button v-if="isAuthenticated && isCommentAuthor(comment.author_id)"
|
||||
class="btn-delete-comment" @click="deleteComment(comment.id)"
|
||||
title="Удалить комментарий">
|
||||
<button v-if="isAuthenticated && isCommentAuthor(comment.author_id)" class="btn-delete-comment"
|
||||
@click="deleteComment(comment.id)" title="Удалить комментарий">
|
||||
🗑️
|
||||
</button>
|
||||
</div>
|
||||
@@ -212,32 +209,31 @@
|
||||
<div class="news-modal-overlay" :class="{ 'active': showNewsFormModal }" v-if="showNewsFormModal">
|
||||
<div class="news-modal">
|
||||
<button class="modal-close" @click="closeNewsFormModal">×</button>
|
||||
|
||||
|
||||
<div class="modal-content">
|
||||
<h2 class="modal-title">{{ editingNews ? '✏️ Редактировать новость' : '📝 Создать новость' }}</h2>
|
||||
|
||||
|
||||
<form @submit.prevent="submitNewsForm" class="news-form">
|
||||
<div class="form-group">
|
||||
<label>Заголовок *</label>
|
||||
<input v-model="newsForm.title" type="text" required
|
||||
placeholder="Введите заголовок новости" class="form-input">
|
||||
<input v-model="newsForm.title" type="text" required placeholder="Введите заголовок новости"
|
||||
class="form-input">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Краткое описание *</label>
|
||||
<textarea v-model="newsForm.excerpt" required
|
||||
placeholder="Краткое описание новости (максимум 500 символов)"
|
||||
class="form-textarea" rows="3" maxlength="500"></textarea>
|
||||
<textarea v-model="newsForm.excerpt" required
|
||||
placeholder="Краткое описание новости (максимум 500 символов)" class="form-textarea" rows="3"
|
||||
maxlength="500"></textarea>
|
||||
<div class="char-count">{{ newsForm.excerpt.length }}/500</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Содержание *</label>
|
||||
<textarea v-model="newsForm.content" required
|
||||
placeholder="Полное содержание новости"
|
||||
class="form-textarea" rows="6"></textarea>
|
||||
<textarea v-model="newsForm.content" required placeholder="Полное содержание новости"
|
||||
class="form-textarea" rows="6"></textarea>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Категория *</label>
|
||||
<select v-model="newsForm.category" required class="form-select">
|
||||
@@ -248,14 +244,13 @@
|
||||
<option value="community">Сообщество</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Изображение (URL)</label>
|
||||
<input v-model="newsForm.image" type="url"
|
||||
placeholder="https://example.com/image.jpg" class="form-input">
|
||||
<input v-model="newsForm.image" type="url" placeholder="https://example.com/image.jpg" class="form-input">
|
||||
<small>Оставьте пустым для изображения по умолчанию</small>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-outline" @click="closeNewsFormModal">
|
||||
Отмена
|
||||
@@ -331,7 +326,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useAuthStore, ['fetchProfile']),
|
||||
|
||||
|
||||
async fetchNews() {
|
||||
this.loading = true
|
||||
this.error = ''
|
||||
@@ -345,7 +340,7 @@ export default {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async fetchComments(newsId) {
|
||||
try {
|
||||
const response = await apiClient.get(`/news/${newsId}/comments`)
|
||||
@@ -355,7 +350,7 @@ export default {
|
||||
this.comments = []
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async openNewsModal(newsItem) {
|
||||
try {
|
||||
const response = await apiClient.get(`/news/${newsItem.id}`)
|
||||
@@ -365,20 +360,20 @@ export default {
|
||||
console.error('Failed to fetch news details:', error)
|
||||
this.selectedNews = newsItem
|
||||
}
|
||||
|
||||
|
||||
this.showNewsModal = true
|
||||
document.body.style.overflow = 'hidden'
|
||||
},
|
||||
|
||||
|
||||
async addComment() {
|
||||
if (!this.newComment.trim() || !this.selectedNews) return
|
||||
|
||||
|
||||
this.commentLoading = true
|
||||
try {
|
||||
await apiClient.post(`/news/${this.selectedNews.id}/comments`, {
|
||||
content: this.newComment
|
||||
})
|
||||
|
||||
|
||||
this.newComment = ''
|
||||
await this.fetchComments(this.selectedNews.id)
|
||||
await this.fetchNews() // Обновляем счетчик комментариев
|
||||
@@ -389,10 +384,10 @@ export default {
|
||||
this.commentLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async deleteComment(commentId) {
|
||||
if (!confirm('Удалить этот комментарий?')) return
|
||||
|
||||
|
||||
try {
|
||||
await apiClient.$.delete(`/news/comments/${commentId}`)
|
||||
await this.fetchComments(this.selectedNews.id)
|
||||
@@ -402,7 +397,7 @@ export default {
|
||||
alert('Не удалось удалить комментарий.')
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
openCreateNewsModal() {
|
||||
this.editingNews = null
|
||||
this.newsForm = {
|
||||
@@ -414,7 +409,7 @@ export default {
|
||||
}
|
||||
this.showNewsFormModal = true
|
||||
},
|
||||
|
||||
|
||||
editNews(newsItem) {
|
||||
this.editingNews = newsItem
|
||||
this.newsForm = {
|
||||
@@ -429,16 +424,40 @@ export default {
|
||||
this.closeNewsModal()
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async checkAuth() {
|
||||
try {
|
||||
const token = localStorage.getItem('auth_token')
|
||||
if (token) {
|
||||
try {
|
||||
// Пытаемся получить профиль пользователя для проверки авторизации
|
||||
const response = await apiClient.get('/user/profile')
|
||||
this.isAuthenticated = true
|
||||
this.currentUser = response.data
|
||||
} catch (error) {
|
||||
console.error('Токен невалиден:', error)
|
||||
this.handleAuthError()
|
||||
}
|
||||
} else {
|
||||
this.isAuthenticated = false
|
||||
this.currentUser = null
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Ошибка проверки авторизации:', error)
|
||||
this.handleAuthError()
|
||||
}
|
||||
},
|
||||
|
||||
async submitNewsForm() {
|
||||
this.newsLoading = true
|
||||
try {
|
||||
if (this.editingNews) {
|
||||
await apiClient.put(`/news/${this.editingNews.id}`, this.newsForm)
|
||||
} else {
|
||||
console.log('News form data:', this.newsForm)
|
||||
await apiClient.post('/news', this.newsForm)
|
||||
}
|
||||
|
||||
|
||||
this.closeNewsFormModal()
|
||||
await this.fetchNews()
|
||||
} catch (error) {
|
||||
@@ -448,10 +467,10 @@ export default {
|
||||
this.newsLoading = false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
async deleteNews(newsId) {
|
||||
if (!confirm('Удалить эту новость? Это действие нельзя отменить.')) return
|
||||
|
||||
|
||||
try {
|
||||
await apiClient.delete(`/news/${newsId}`)
|
||||
await this.fetchNews()
|
||||
@@ -463,24 +482,24 @@ export default {
|
||||
alert('Не удалось удалить новость.')
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
isAuthor(authorId) {
|
||||
return this.user && this.user.id === authorId
|
||||
},
|
||||
|
||||
|
||||
isCommentAuthor(authorId) {
|
||||
return this.user && this.user.id === authorId
|
||||
},
|
||||
|
||||
|
||||
setFilter(filter) {
|
||||
this.activeFilter = filter
|
||||
this.visibleNews = 6
|
||||
},
|
||||
|
||||
|
||||
loadMore() {
|
||||
this.visibleNews += 3
|
||||
},
|
||||
|
||||
|
||||
closeNewsModal() {
|
||||
this.showNewsModal = false
|
||||
this.selectedNews = null
|
||||
@@ -488,12 +507,12 @@ export default {
|
||||
this.newComment = ''
|
||||
document.body.style.overflow = ''
|
||||
},
|
||||
|
||||
|
||||
closeNewsFormModal() {
|
||||
this.showNewsFormModal = false
|
||||
this.editingNews = null
|
||||
},
|
||||
|
||||
|
||||
shareNews(newsItem) {
|
||||
if (navigator.share) {
|
||||
navigator.share({
|
||||
@@ -506,7 +525,7 @@ export default {
|
||||
alert('Ссылка скопирована в буфер обмена!')
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getCategoryLabel(category) {
|
||||
const labels = {
|
||||
'events': 'События',
|
||||
@@ -516,7 +535,7 @@ export default {
|
||||
}
|
||||
return labels[category] || 'Новости'
|
||||
},
|
||||
|
||||
|
||||
formatDate(dateString) {
|
||||
const date = new Date(dateString)
|
||||
return date.toLocaleDateString('ru-RU', {
|
||||
@@ -525,17 +544,17 @@ export default {
|
||||
year: 'numeric'
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
getImageUrl(imagePath) {
|
||||
if (!imagePath) return '/images/default-news.jpg'
|
||||
if (imagePath.startsWith('http')) return imagePath
|
||||
return `https://begushiybashkir.ru${imagePath}`
|
||||
},
|
||||
|
||||
|
||||
formatContent(content) {
|
||||
return content.replace(/\n/g, '<br>')
|
||||
},
|
||||
|
||||
|
||||
handleKeydown(event) {
|
||||
if (event.key === 'Escape') {
|
||||
if (this.showNewsModal) this.closeNewsModal()
|
||||
@@ -549,6 +568,7 @@ export default {
|
||||
if (this.isAuthenticated) {
|
||||
await this.fetchProfile()
|
||||
}
|
||||
this.checkAuth()
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.removeEventListener('keydown', this.handleKeydown)
|
||||
@@ -573,7 +593,9 @@ export default {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.btn-edit, .btn-delete, .btn-delete-comment {
|
||||
.btn-edit,
|
||||
.btn-delete,
|
||||
.btn-delete-comment {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 5px;
|
||||
@@ -588,7 +610,8 @@ export default {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.btn-delete:hover, .btn-delete-comment:hover {
|
||||
.btn-delete:hover,
|
||||
.btn-delete-comment:hover {
|
||||
background: #ffebee;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
@@ -602,7 +625,9 @@ export default {
|
||||
background: #d32f2f;
|
||||
}
|
||||
|
||||
.loading-state, .error-state, .empty-state {
|
||||
.loading-state,
|
||||
.error-state,
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 3rem;
|
||||
color: #666;
|
||||
@@ -619,8 +644,13 @@ export default {
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.error-state {
|
||||
@@ -730,7 +760,9 @@ export default {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.form-input, .form-textarea, .form-select {
|
||||
.form-input,
|
||||
.form-textarea,
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 2px solid #e9ecef;
|
||||
@@ -739,7 +771,9 @@ export default {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-input:focus, .form-textarea:focus, .form-select:focus {
|
||||
.form-input:focus,
|
||||
.form-textarea:focus,
|
||||
.form-select:focus {
|
||||
outline: none;
|
||||
border-color: #2e8b57;
|
||||
}
|
||||
@@ -783,20 +817,20 @@ export default {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
|
||||
.news-owner-actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
||||
.form-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
||||
.modal-owner-actions {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.modal-owner-actions .btn {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user