modified: serv_nginx/bbvue/src/views/News.vue

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