add_notification_detail_page
This commit is contained in:
		
							parent
							
								
									473f2ceaf5
								
							
						
					
					
						commit
						5b6f3bd1dd
					
				
							
								
								
									
										207
									
								
								app/static/css/notification_detail.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								app/static/css/notification_detail.css
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,207 @@
 | 
				
			|||||||
 | 
					/* Notification Detail Styles - Matches the existing theme */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					:root {
 | 
				
			||||||
 | 
					    --mint-green: #A8E6CF;
 | 
				
			||||||
 | 
					    --pale-yellow: #FFD3B6;
 | 
				
			||||||
 | 
					    --coral-pink: #FFAAA5;
 | 
				
			||||||
 | 
					    --sky-blue: #BDE4F4;
 | 
				
			||||||
 | 
					    --clean-white: #FFFFFF;
 | 
				
			||||||
 | 
					    --bright-orange: #FF8C69;
 | 
				
			||||||
 | 
					    --lemon-yellow: #FFFACD;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --text-dark: #424242;
 | 
				
			||||||
 | 
					    --text-medium: #757575;
 | 
				
			||||||
 | 
					    --text-light: #9E9E9E;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --font-title: 'Poppins', sans-serif;
 | 
				
			||||||
 | 
					    --font-body: 'Nunito Sans', sans-serif;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    --card-shadow: 0 4px 15px rgba(0, 0, 0, 0.06);
 | 
				
			||||||
 | 
					    --card-hover-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
					    --border-radius-main: 12px;
 | 
				
			||||||
 | 
					    --border-radius-small: 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Container for detail page */
 | 
				
			||||||
 | 
					.notification-detail-container {
 | 
				
			||||||
 | 
					    padding: 25px 30px;
 | 
				
			||||||
 | 
					    max-width: 900px;
 | 
				
			||||||
 | 
					    margin: 30px auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Main notification card */
 | 
				
			||||||
 | 
					.notification-card {
 | 
				
			||||||
 | 
					    background-color: var(--clean-white);
 | 
				
			||||||
 | 
					    border-radius: var(--border-radius-main);
 | 
				
			||||||
 | 
					    box-shadow: var(--card-shadow);
 | 
				
			||||||
 | 
					    padding: 30px;
 | 
				
			||||||
 | 
					    border-left: 5px solid var(--sky-blue);
 | 
				
			||||||
 | 
					    transition: box-shadow 0.3s ease;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-card:hover {
 | 
				
			||||||
 | 
					    box-shadow: var(--card-hover-shadow);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-card.unread {
 | 
				
			||||||
 | 
					    border-left-color: var(--mint-green);
 | 
				
			||||||
 | 
					    background-color: #f6fffb;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Header section */
 | 
				
			||||||
 | 
					.notification-header {
 | 
				
			||||||
 | 
					    margin-bottom: 30px;
 | 
				
			||||||
 | 
					    border-bottom: 1px solid #f0f0f0;
 | 
				
			||||||
 | 
					    padding-bottom: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-meta-top {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    justify-content: space-between;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    margin-bottom: 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-type {
 | 
				
			||||||
 | 
					    background-color: var(--sky-blue);
 | 
				
			||||||
 | 
					    color: #3E84A8;
 | 
				
			||||||
 | 
					    padding: 5px 15px;
 | 
				
			||||||
 | 
					    border-radius: 20px;
 | 
				
			||||||
 | 
					    font-weight: 600;
 | 
				
			||||||
 | 
					    font-size: 0.85rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-status .unread-badge {
 | 
				
			||||||
 | 
					    background-color: var(--bright-orange);
 | 
				
			||||||
 | 
					    color: white;
 | 
				
			||||||
 | 
					    font-size: 0.8rem;
 | 
				
			||||||
 | 
					    padding: 5px 15px;
 | 
				
			||||||
 | 
					    border-radius: 20px;
 | 
				
			||||||
 | 
					    font-weight: 600;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-status .read-badge {
 | 
				
			||||||
 | 
					    background-color: #e0e0e0;
 | 
				
			||||||
 | 
					    color: var(--text-medium);
 | 
				
			||||||
 | 
					    font-size: 0.8rem;
 | 
				
			||||||
 | 
					    padding: 5px 15px;
 | 
				
			||||||
 | 
					    border-radius: 20px;
 | 
				
			||||||
 | 
					    font-weight: 600;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-title {
 | 
				
			||||||
 | 
					    font-family: var(--font-title);
 | 
				
			||||||
 | 
					    font-size: 1.8rem;
 | 
				
			||||||
 | 
					    font-weight: 600;
 | 
				
			||||||
 | 
					    color: var(--text-dark);
 | 
				
			||||||
 | 
					    margin: 15px 0;
 | 
				
			||||||
 | 
					    line-height: 1.3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-time {
 | 
				
			||||||
 | 
					    color: var(--text-light);
 | 
				
			||||||
 | 
					    font-size: 0.9rem;
 | 
				
			||||||
 | 
					    font-style: italic;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-time i {
 | 
				
			||||||
 | 
					    margin-right: 5px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Content section */
 | 
				
			||||||
 | 
					.notification-content {
 | 
				
			||||||
 | 
					    margin-bottom: 30px;
 | 
				
			||||||
 | 
					    padding: 20px 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-body {
 | 
				
			||||||
 | 
					    font-family: var(--font-body);
 | 
				
			||||||
 | 
					    color: var(--text-medium);
 | 
				
			||||||
 | 
					    line-height: 1.8;
 | 
				
			||||||
 | 
					    font-size: 1.05rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-body p {
 | 
				
			||||||
 | 
					    margin-bottom: 20px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.notification-body h2, .notification-body h3 {
 | 
				
			||||||
 | 
					    font-family: var(--font-title);
 | 
				
			||||||
 | 
					    color: var(--text-dark);
 | 
				
			||||||
 | 
					    margin-top: 25px;
 | 
				
			||||||
 | 
					    margin-bottom: 15px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Footer with buttons */
 | 
				
			||||||
 | 
					.notification-footer {
 | 
				
			||||||
 | 
					    display: flex;
 | 
				
			||||||
 | 
					    justify-content: space-between;
 | 
				
			||||||
 | 
					    padding-top: 20px;
 | 
				
			||||||
 | 
					    border-top: 1px solid #f0f0f0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn {
 | 
				
			||||||
 | 
					    padding: 10px 20px;
 | 
				
			||||||
 | 
					    border-radius: 25px;
 | 
				
			||||||
 | 
					    font-family: var(--font-body);
 | 
				
			||||||
 | 
					    font-weight: 600;
 | 
				
			||||||
 | 
					    text-decoration: none;
 | 
				
			||||||
 | 
					    transition: all 0.3s ease;
 | 
				
			||||||
 | 
					    font-size: 0.95rem;
 | 
				
			||||||
 | 
					    cursor: pointer;
 | 
				
			||||||
 | 
					    display: inline-flex;
 | 
				
			||||||
 | 
					    align-items: center;
 | 
				
			||||||
 | 
					    justify-content: center;
 | 
				
			||||||
 | 
					    border: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn i {
 | 
				
			||||||
 | 
					    margin-right: 8px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-back {
 | 
				
			||||||
 | 
					    background-color: #f0f0f0;
 | 
				
			||||||
 | 
					    color: var(--text-medium);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-back:hover {
 | 
				
			||||||
 | 
					    background-color: #e0e0e0;
 | 
				
			||||||
 | 
					    color: var(--text-dark);
 | 
				
			||||||
 | 
					    transform: translateY(-2px);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-mark-read {
 | 
				
			||||||
 | 
					    background-color: var(--mint-green);
 | 
				
			||||||
 | 
					    color: #2A7F62;
 | 
				
			||||||
 | 
					    box-shadow: 0 2px 8px rgba(168, 230, 207, 0.3);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.btn-mark-read:hover {
 | 
				
			||||||
 | 
					    background-color: #98d8c0;
 | 
				
			||||||
 | 
					    transform: translateY(-2px);
 | 
				
			||||||
 | 
					    box-shadow: 0 4px 12px rgba(168, 230, 207, 0.4);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Responsive adjustments */
 | 
				
			||||||
 | 
					@media (max-width: 768px) {
 | 
				
			||||||
 | 
					    .notification-detail-container {
 | 
				
			||||||
 | 
					        padding: 15px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .notification-card {
 | 
				
			||||||
 | 
					        padding: 20px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .notification-title {
 | 
				
			||||||
 | 
					        font-size: 1.5rem;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .notification-footer {
 | 
				
			||||||
 | 
					        flex-direction: column;
 | 
				
			||||||
 | 
					        gap: 15px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .btn {
 | 
				
			||||||
 | 
					        width: 100%;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								app/static/js/notification_detail.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								app/static/js/notification_detail.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					document.addEventListener('DOMContentLoaded', function() {
 | 
				
			||||||
 | 
					    const markAsReadBtn = document.getElementById('markAsReadBtn');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (markAsReadBtn) {
 | 
				
			||||||
 | 
					        // 获取通知ID
 | 
				
			||||||
 | 
					        const notificationCard = document.querySelector('.notification-card');
 | 
				
			||||||
 | 
					        const notificationId = notificationCard ? notificationCard.dataset.id : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (notificationId) {
 | 
				
			||||||
 | 
					            // 添加点击事件监听器
 | 
				
			||||||
 | 
					            markAsReadBtn.addEventListener('click', function() {
 | 
				
			||||||
 | 
					                markAsRead(notificationId);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 页面加载时自动标记为已读(可选功能,取决于需求)
 | 
				
			||||||
 | 
					            // 如果想要在用户打开通知详情页时自动标记为已读,取消下面的注释
 | 
				
			||||||
 | 
					            // markAsRead(notificationId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 标记通知为已读的函数
 | 
				
			||||||
 | 
					    function markAsRead(notificationId) {
 | 
				
			||||||
 | 
					        fetch(`/announcement/notification/${notificationId}/mark-read`, {
 | 
				
			||||||
 | 
					            method: 'POST',
 | 
				
			||||||
 | 
					            headers: {
 | 
				
			||||||
 | 
					                'Content-Type': 'application/json',
 | 
				
			||||||
 | 
					                'X-Requested-With': 'XMLHttpRequest'
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .then(response => response.json())
 | 
				
			||||||
 | 
					        .then(data => {
 | 
				
			||||||
 | 
					            if (data.success) {
 | 
				
			||||||
 | 
					                // 更新UI
 | 
				
			||||||
 | 
					                const notificationCard = document.querySelector('.notification-card');
 | 
				
			||||||
 | 
					                if (notificationCard) {
 | 
				
			||||||
 | 
					                    notificationCard.classList.remove('unread');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 更新状态标签
 | 
				
			||||||
 | 
					                const statusContainer = document.querySelector('.notification-status');
 | 
				
			||||||
 | 
					                if (statusContainer) {
 | 
				
			||||||
 | 
					                    statusContainer.innerHTML = '<span class="read-badge">已读</span>';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 隐藏标记为已读按钮
 | 
				
			||||||
 | 
					                if (markAsReadBtn) {
 | 
				
			||||||
 | 
					                    markAsReadBtn.style.display = 'none';
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // 更新通知计数(如果有导航栏通知计数)
 | 
				
			||||||
 | 
					                updateNotificationCount();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .catch(error => console.error('Error:', error));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 更新通知计数
 | 
				
			||||||
 | 
					    function updateNotificationCount() {
 | 
				
			||||||
 | 
					        fetch('/announcement/notifications/count')
 | 
				
			||||||
 | 
					            .then(response => response.json())
 | 
				
			||||||
 | 
					            .then(data => {
 | 
				
			||||||
 | 
					                const badge = document.querySelector('.notifications .badge');
 | 
				
			||||||
 | 
					                if (data.count > 0) {
 | 
				
			||||||
 | 
					                    if (badge) {
 | 
				
			||||||
 | 
					                        badge.textContent = data.count;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        const notificationsElement = document.querySelector('.notifications');
 | 
				
			||||||
 | 
					                        if (notificationsElement) {
 | 
				
			||||||
 | 
					                            const newBadge = document.createElement('span');
 | 
				
			||||||
 | 
					                            newBadge.className = 'badge';
 | 
				
			||||||
 | 
					                            newBadge.textContent = data.count;
 | 
				
			||||||
 | 
					                            notificationsElement.appendChild(newBadge);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    if (badge) {
 | 
				
			||||||
 | 
					                        badge.remove();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .catch(error => console.error('Error:', error));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										55
									
								
								app/templates/announcement/notification_detail.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								app/templates/announcement/notification_detail.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					{% extends 'base.html' %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block title %}通知详情 - 图书管理系统{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block head %}
 | 
				
			||||||
 | 
					<link rel="preconnect" href="https://fonts.googleapis.com">
 | 
				
			||||||
 | 
					<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
 | 
				
			||||||
 | 
					<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&family=Nunito+Sans:wght@400;600&display=swap" rel="stylesheet">
 | 
				
			||||||
 | 
					<link rel="stylesheet" href="{{ url_for('static', filename='css/notification_detail.css') }}">
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					<div class="notification-detail-container">
 | 
				
			||||||
 | 
					    <div class="notification-card {% if notification.status == 0 %}unread{% endif %}" data-id="{{ notification.id }}">
 | 
				
			||||||
 | 
					        <div class="notification-header">
 | 
				
			||||||
 | 
					            <div class="notification-meta-top">
 | 
				
			||||||
 | 
					                <span class="notification-type">{{ notification.type }}</span>
 | 
				
			||||||
 | 
					                <span class="notification-status">
 | 
				
			||||||
 | 
					                    {% if notification.status == 0 %}
 | 
				
			||||||
 | 
					                        <span class="unread-badge">未读</span>
 | 
				
			||||||
 | 
					                    {% else %}
 | 
				
			||||||
 | 
					                        <span class="read-badge">已读</span>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                </span>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <h1 class="notification-title">{{ notification.title }}</h1>
 | 
				
			||||||
 | 
					            <div class="notification-time">
 | 
				
			||||||
 | 
					                <i class="far fa-clock"></i> {{ notification.created_at.strftime('%Y-%m-%d %H:%M') }}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="notification-content">
 | 
				
			||||||
 | 
					            <div class="notification-body">
 | 
				
			||||||
 | 
					                {{ notification.content|safe }}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="notification-footer">
 | 
				
			||||||
 | 
					            <a href="{{ url_for('announcement.user_notifications') }}" class="btn btn-back">
 | 
				
			||||||
 | 
					                <i class="fas fa-arrow-left"></i> 返回通知列表
 | 
				
			||||||
 | 
					            </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            {% if notification.status == 0 %}
 | 
				
			||||||
 | 
					                <button class="btn btn-mark-read" id="markAsReadBtn">
 | 
				
			||||||
 | 
					                    <i class="fas fa-check"></i> 标记为已读
 | 
				
			||||||
 | 
					                </button>
 | 
				
			||||||
 | 
					            {% endif %}
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block scripts %}
 | 
				
			||||||
 | 
					<script src="{{ url_for('static', filename='js/notification_detail.js') }}"></script>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										1376
									
								
								code_collection.txt
									
									
									
									
									
								
							
							
						
						
									
										1376
									
								
								code_collection.txt
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user