kevinwang676 commited on
Commit
a8352ae
·
verified ·
1 Parent(s): ead231b

Create live2dcubismcore.js

Browse files
Files changed (1) hide show
  1. live2dcubismcore.js +577 -0
live2dcubismcore.js ADDED
@@ -0,0 +1,577 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+ <head>
4
+ <!-- Google Analytics tag (gtag.js) -->
5
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-Y2Z5KKXXL7"></script>
6
+ <script>
7
+ window.dataLayer = window.dataLayer || [];
8
+ function gtag(){dataLayer.push(arguments);}
9
+ gtag('js', new Date());
10
+
11
+ gtag('config', 'G-Y2Z5KKXXL7');
12
+ </script>
13
+
14
+ <meta charset="UTF-8">
15
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
16
+ <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><g transform='translate(256,256) scale(1.1) translate(-256,-256)'><path fill='%2387CEEB' d='M411.1,217.9c-9.7-43.5-48.4-76.2-94.6-76.2c-28.6,0-54.3,12.9-71.9,33.2c-9.2-3.2-19.1-5-29.4-5 c-32.1,0-58.4,24.7-62.3,56.5c-0.6,0-1.2,0-1.8,0c-38.6,0-70.1,31.6-70.1,70.1c0,38.5,31.5,70,70.1,70h229.9 c38.5,0,70.1-31.5,70.1-70C480,248.9,449.8,217.9,411.1,217.9z'/></g></svg>">
17
+ <title>书梦 - 书写你的每一个梦</title>
18
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
19
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/animate.min.css" rel="stylesheet">
20
+ <link href="https://cdn.jsdelivr.net/npm/@fortawesome/[email protected]/css/all.min.css" rel="stylesheet">
21
+ <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;700&display=swap" rel="stylesheet">
22
+ <link href="/static/live2d/js/live2d.css" rel="stylesheet">
23
+ <style>
24
+ :root {
25
+ --primary-color: #FF6B6B;
26
+ --secondary-color: #4ECDC4;
27
+ --accent-color: #FFE66D;
28
+ --text-color: #2C3E50;
29
+ --bg-color: #F7F9FC;
30
+ --chat-bg: #FFFFFF;
31
+ --message-user: #E3F2FD;
32
+ --message-ai: #F5F5F5;
33
+ }
34
+
35
+ body {
36
+ font-family: 'Noto Serif SC', "PingFang SC", "Microsoft YaHei", serif;
37
+ background-color: var(--bg-color);
38
+ color: var(--text-color);
39
+ line-height: 1.8;
40
+ }
41
+
42
+ .navbar {
43
+ background: rgba(255, 255, 255, 0.95);
44
+ box-shadow: 0 2px 15px rgba(0, 0, 0, 0.1);
45
+ padding-top: 0.3rem;
46
+ padding-bottom: 0.3rem;
47
+ }
48
+
49
+ .navbar-brand {
50
+ font-size: 2rem;
51
+ font-weight: bold;
52
+ background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
53
+ -webkit-background-clip: text;
54
+ background-clip: text;
55
+ -webkit-text-fill-color: transparent;
56
+ padding: 0.3rem 0;
57
+ }
58
+
59
+ .brand-subtitle {
60
+ font-size: 1.05rem;
61
+ color: #FF6B6B;
62
+ font-weight: bold;
63
+ opacity: 0.8;
64
+ margin: 0;
65
+ padding: 0.3rem 0;
66
+ position: absolute;
67
+ left: 50%;
68
+ transform: translateX(-50%);
69
+ white-space: nowrap;
70
+ }
71
+
72
+ .brand-subtitle2 {
73
+ font-size: 1rem;
74
+ color: #FF6B6B;
75
+ }
76
+
77
+ .hero-section {
78
+ background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
79
+ position: relative;
80
+ overflow: hidden;
81
+ padding: 8rem 0;
82
+ margin-bottom: -6rem;
83
+ }
84
+
85
+ .hero-section::before {
86
+ content: '';
87
+ position: absolute;
88
+ top: 0;
89
+ left: 0;
90
+ right: 0;
91
+ bottom: 0;
92
+ background: url('data:image/svg+xml,<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><circle cx="2" cy="2" r="1" fill="rgba(255,255,255,0.1)"/></svg>') repeat;
93
+ opacity: 0.3;
94
+ animation: float 20s linear infinite;
95
+ }
96
+
97
+ @keyframes float {
98
+ from { transform: translate(0, 0); }
99
+ to { transform: translate(20px, 20px); }
100
+ }
101
+
102
+ .chat-container {
103
+ background: var(--chat-bg);
104
+ border-radius: 20px;
105
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
106
+ margin-top: -2rem;
107
+ position: relative;
108
+ z-index: 1;
109
+ min-height: 600px;
110
+ }
111
+
112
+ .chat-messages {
113
+ height: 400px;
114
+ overflow-y: auto;
115
+ padding: 1.5rem;
116
+ }
117
+
118
+ .message {
119
+ margin-bottom: 1.5rem;
120
+ max-width: 80%;
121
+ position: relative;
122
+ }
123
+
124
+ .message-user {
125
+ margin-left: auto;
126
+ background: var(--message-user);
127
+ border-radius: 20px 20px 5px 20px;
128
+ padding: 1rem 1.5rem;
129
+ }
130
+
131
+ .message-ai {
132
+ margin-right: auto;
133
+ background: var(--message-ai);
134
+ border-radius: 20px 20px 20px 5px;
135
+ padding: 1rem 1.5rem;
136
+ }
137
+
138
+ .chat-input {
139
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
140
+ padding: 1.5rem;
141
+ background: white;
142
+ border-radius: 0 0 20px 20px;
143
+ }
144
+
145
+ .btn-primary {
146
+ background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
147
+ border: none;
148
+ border-radius: 25px;
149
+ padding: 0.8rem 2rem;
150
+ font-weight: 600;
151
+ transition: all 0.3s ease;
152
+ }
153
+
154
+ .btn-primary:hover {
155
+ transform: translateY(-2px);
156
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
157
+ }
158
+
159
+ .form-control {
160
+ border-radius: 15px;
161
+ padding: 0.8rem 1.2rem;
162
+ border: 2px solid #eee;
163
+ transition: all 0.3s ease;
164
+ }
165
+
166
+ .form-control:focus {
167
+ border-color: var(--secondary-color);
168
+ box-shadow: 0 0 0 0.2rem rgba(78, 205, 196, 0.25);
169
+ }
170
+
171
+ .feature-card {
172
+ background: white;
173
+ border-radius: 20px;
174
+ padding: 2rem;
175
+ text-align: center;
176
+ transition: all 0.3s ease;
177
+ border: none;
178
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
179
+ }
180
+
181
+ .feature-card:hover {
182
+ transform: translateY(-10px);
183
+ box-shadow: 0 15px 30px rgba(0, 0, 0, 0.12);
184
+ }
185
+
186
+ .feature-icon {
187
+ width: 80px;
188
+ height: 80px;
189
+ border-radius: 50%;
190
+ display: flex;
191
+ align-items: center;
192
+ justify-content: center;
193
+ margin: 0 auto 1.5rem;
194
+ background: linear-gradient(45deg, var(--primary-color), var(--secondary-color));
195
+ color: white;
196
+ font-size: 2rem;
197
+ }
198
+
199
+ .footer {
200
+ background: var(--text-color);
201
+ color: white;
202
+ padding: 4rem 0 2rem;
203
+ margin-top: 4rem;
204
+ }
205
+
206
+ .social-links a {
207
+ display: inline-flex;
208
+ align-items: center;
209
+ justify-content: center;
210
+ width: 40px;
211
+ height: 40px;
212
+ border-radius: 50%;
213
+ background: rgba(255, 255, 255, 0.1);
214
+ color: white;
215
+ margin: 0 0.5rem;
216
+ transition: all 0.3s ease;
217
+ }
218
+
219
+ .social-links a:hover {
220
+ background: var(--secondary-color);
221
+ transform: translateY(-3px);
222
+ }
223
+
224
+ .nav-link {
225
+ color: var(--text-color) !important;
226
+ font-weight: 500;
227
+ padding: 0.5rem 1.2rem;
228
+ border-radius: 20px;
229
+ transition: all 0.3s ease;
230
+ }
231
+
232
+ .nav-link:hover {
233
+ background: rgba(255, 107, 107, 0.1);
234
+ color: var(--primary-color) !important;
235
+ }
236
+
237
+ .loading-dots:after {
238
+ content: '.';
239
+ animation: dots 1.5s steps(5, end) infinite;
240
+ }
241
+
242
+ @keyframes dots {
243
+ 0%, 20% { content: '.'; }
244
+ 40% { content: '..'; }
245
+ 60% { content: '...'; }
246
+ 80% { content: '....'; }
247
+ 100% { content: '.....'; }
248
+ }
249
+
250
+ .fancy-poetry2 {
251
+ font-family: "STKaiti", "KaiTi", serif;
252
+ background: linear-gradient(120deg, #4568dc, #b06ab3);
253
+ background-clip: text;
254
+ -webkit-background-clip: text;
255
+ -webkit-text-fill-color: transparent;
256
+ font-size: 1.25rem;
257
+ letter-spacing: 1px;
258
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
259
+ display: inline-block;
260
+ vertical-align: middle;
261
+ line-height: normal;
262
+ margin-bottom: 7px;
263
+ }
264
+
265
+ .navbar-toggler {
266
+ border: none;
267
+ padding: 0.5rem;
268
+ color: var(--text-color);
269
+ background: transparent;
270
+ }
271
+
272
+ .navbar-toggler:focus {
273
+ box-shadow: none;
274
+ outline: none;
275
+ }
276
+
277
+ .navbar-toggler-icon {
278
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba(44, 62, 80, 1)' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
279
+ width: 24px;
280
+ height: 24px;
281
+ }
282
+
283
+ @media (max-width: 991.98px) {
284
+ .navbar-nav {
285
+ background: rgba(255, 255, 255, 0.98);
286
+ padding: 1rem;
287
+ border-radius: 10px;
288
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
289
+ margin-top: 1rem;
290
+ }
291
+
292
+ .nav-item {
293
+ margin: 0.5rem 0;
294
+ }
295
+
296
+ .brand-subtitle {
297
+ display: none;
298
+ }
299
+ }
300
+ </style>
301
+ {% block extra_css %}{% endblock %}
302
+ </head>
303
+ <body>
304
+ <nav class="navbar navbar-expand-lg fixed-top">
305
+ <div class="container">
306
+ <a class="navbar-brand" href="/">
307
+ <i class="fas fa-feather-alt me-2"></i>书梦
308
+ </a>
309
+ <div class="brand-subtitle">创造你的热爱</div>
310
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
311
+ <span class="navbar-toggler-icon"></span>
312
+ </button>
313
+ <div class="collapse navbar-collapse" id="navbarNav">
314
+ <ul class="navbar-nav ms-auto">
315
+ <li class="nav-item">
316
+ <a class="nav-link" href="/"><i class="fas fa-home me-1"></i>首页</a>
317
+ </li>
318
+ <li class="nav-item">
319
+ <a class="nav-link" href="{{ url_for('text_to_speech_page') }}"><i class="fas fa-microphone me-1"></i><span class="fancy-poetry2">梦语</span></a>
320
+ </li>
321
+ <li class="nav-item">
322
+ <a class="nav-link" href="{{ url_for('forum') }}"><i class="fas fa-comments me-1"></i><span class="fancy-poetry2">书梦社</span></a>
323
+ </li>
324
+ {% if current_user.is_authenticated %}
325
+ <li class="nav-item dropdown position-relative">
326
+ {% if request.endpoint != 'index' %}
327
+ <a class="nav-link" href="#" id="notificationsDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
328
+ <i class="fas fa-bell"></i>
329
+ <span id="notificationCount" class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger" style="display: none;">
330
+ 0
331
+ </span>
332
+ </a>
333
+ <div class="dropdown-menu dropdown-menu-end" aria-labelledby="notificationsDropdown" style="width: 300px; max-height: 400px; overflow-y: auto;">
334
+ <div id="notificationList" class="px-2">
335
+ <!-- Notifications will be loaded here -->
336
+ </div>
337
+ </div>
338
+ {% endif %}
339
+ </li>
340
+ <li class="nav-item">
341
+ <a class="nav-link" href="/my-stories"><i class="fas fa-book me-1"></i>我的梦</a>
342
+ </li>
343
+ <li class="nav-item">
344
+ <a class="nav-link" href="/settings"><i class="fas fa-cog me-1"></i>设置</a>
345
+ </li>
346
+ {% else %}
347
+ <li class="nav-item">
348
+ <a class="nav-link" href="/login"><i class="fas fa-sign-in-alt me-1"></i>登录</a>
349
+ </li>
350
+ {% endif %}
351
+ </ul>
352
+ </div>
353
+ </div>
354
+ </nav>
355
+
356
+ {% block content %}{% endblock %}
357
+
358
+ <footer class="footer">
359
+ <div class="container">
360
+ <div class="row">
361
+ <div class="col-md-4 mb-4">
362
+ <h4><i class="fas fa-feather-alt me-2"></i>书梦</h4>
363
+ <p class="mb-0 brand-subtitle2" style="margin-top: 10px; letter-spacing: 0.2em;">创造你的热爱</p>
364
+ <p class="mb-0" style="margin-top: 10px;">宇宙这么大,总有一个属于你的梦</p>
365
+ </div>
366
+ <div class="col-md-4 mb-4">
367
+ <h5>快速链接</h5>
368
+ <ul class="list-unstyled">
369
+ <li><a href="/" class="text-white text-decoration-none">首页</a></li>
370
+ <li><a href="/login" class="text-white text-decoration-none">登录</a></li>
371
+ <li><a href="https://www.bilibili.com/video/BV1JBPkeQEt5" target="_blank" class="text-white text-decoration-none">使用指南</a></li>
372
+ </ul>
373
+ </div>
374
+ <div class="col-md-4 mb-4">
375
+ <h5>关注我们</h5>
376
+ <div class="social-links mt-3">
377
+ <a href="#" data-bs-toggle="modal" data-bs-target="#weixinQRModal"><i class="fab fa-weixin"></i></a>
378
+ <a href="https://space.bilibili.com/3546380060592726"><i class="fab fa-bilibili"></i></a>
379
+ <p class="mb-0" style="margin-top: 10px;">官方邮箱:[email protected]</p>
380
+ </div>
381
+ </div>
382
+ </div>
383
+ <div class="text-center mt-4">
384
+ <p class="mb-0">© 2025 书梦 DoingDream. All rights reserved.</p>
385
+ </div>
386
+ </div>
387
+ </footer>
388
+
389
+ <!-- Add the QR code modal -->
390
+ <div class="modal fade" id="weixinQRModal" tabindex="-1" aria-labelledby="weixinQRModalLabel" aria-hidden="true">
391
+ <div class="modal-dialog modal-dialog-centered">
392
+ <div class="modal-content">
393
+ <div class="modal-header">
394
+ <h5 class="modal-title" id="weixinQRModalLabel">微信扫码关注我们吧~</h5>
395
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
396
+ </div>
397
+ <div class="modal-body text-center">
398
+ <img src="{{ url_for('static', filename='qrcode.jpg') }}" alt="WeChat QR Code" class="img-fluid">
399
+ </div>
400
+ </div>
401
+ </div>
402
+ </div>
403
+
404
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
405
+ <script src="/static/live2d/core/live2dcubismcore.min.js"></script>
406
+ <script src="/static/live2d/js/live2d.js"></script>
407
+ {% block extra_js %}
408
+ <script>
409
+ // Add notification handling functions
410
+ function updateNotificationCount() {
411
+ fetch('/notifications/count')
412
+ .then(response => response.json())
413
+ .then(data => {
414
+ const countBadge = document.getElementById('notificationCount');
415
+ if (data.count > 0) {
416
+ countBadge.textContent = data.count;
417
+ countBadge.style.display = 'inline-block';
418
+ } else {
419
+ countBadge.style.display = 'none';
420
+ }
421
+ });
422
+ }
423
+
424
+ // Function to get post_id for a comment
425
+ async function getPostIdForComment(comment_id) {
426
+ try {
427
+ const response = await fetch(`/get_post_id_for_comment/${comment_id}`);
428
+ const data = await response.json();
429
+ return data.post_id;
430
+ } catch (error) {
431
+ console.error('Error getting post_id for comment:', error);
432
+ return null;
433
+ }
434
+ }
435
+
436
+ async function loadNotifications() {
437
+ try {
438
+ const response = await fetch('/notifications');
439
+ const data = await response.json();
440
+ const notificationList = document.getElementById('notificationList');
441
+ notificationList.innerHTML = '';
442
+
443
+ if (data.notifications.length === 0) {
444
+ notificationList.innerHTML = '<div class="text-center py-3">暂无新消息</div>';
445
+ return;
446
+ }
447
+
448
+ // Create a map to store post IDs for comments to avoid multiple requests
449
+ const commentPostIds = new Map();
450
+
451
+ // First, gather all comment IDs that need post IDs
452
+ const commentPromises = data.notifications
453
+ .filter(n => n.comment_id && !n.post_id)
454
+ .map(async n => {
455
+ if (!commentPostIds.has(n.comment_id)) {
456
+ try {
457
+ const response = await fetch(`/get_post_id_for_comment/${n.comment_id}`);
458
+ const data = await response.json();
459
+ commentPostIds.set(n.comment_id, data.post_id);
460
+ } catch (error) {
461
+ console.error('Error fetching post ID:', error);
462
+ commentPostIds.set(n.comment_id, null);
463
+ }
464
+ }
465
+ });
466
+
467
+ // Wait for all post ID requests to complete
468
+ await Promise.all(commentPromises);
469
+
470
+ // Now render all notifications
471
+ for (const notification of data.notifications) {
472
+ let message = '';
473
+ let link = '#';
474
+
475
+ switch (notification.type) {
476
+ case 'follow':
477
+ message = `${notification.sender.username} 关注了你`;
478
+ link = `/user/${notification.sender.id}/profile`;
479
+ break;
480
+ case 'like':
481
+ if (notification.comment_id) {
482
+ message = `${notification.sender.username} 点赞了你的评论`;
483
+ const post_id = notification.post_id || commentPostIds.get(notification.comment_id);
484
+ link = `/forum/post/${post_id}#comment-${notification.comment_id}`;
485
+ } else {
486
+ message = `${notification.sender.username} 点赞了你的分享`;
487
+ link = `/forum/post/${notification.post_id}`;
488
+ }
489
+ break;
490
+ case 'comment':
491
+ if (notification.comment_id) {
492
+ message = `${notification.sender.username} 回复了你的评论`;
493
+ const post_id = notification.post_id || commentPostIds.get(notification.comment_id);
494
+ link = `/forum/post/${post_id}#comment-${notification.comment_id}`;
495
+ } else {
496
+ message = `${notification.sender.username} 评论了你的分享`;
497
+ link = `/forum/post/${notification.post_id}`;
498
+ }
499
+ break;
500
+ }
501
+
502
+ const notificationHtml = `
503
+ <a href="${link}" class="dropdown-item py-2 border-bottom" onclick="handleNotificationClick(event, '${link}')">
504
+ <div class="d-flex align-items-center">
505
+ <div class="flex-shrink-0">
506
+ <img src="${notification.sender.avatar_url ? notification.sender.avatar_url : notification.sender.get_avatar()}"
507
+ class="rounded-circle"
508
+ width="40" height="40"
509
+ alt="${notification.sender.username}"
510
+ style="object-fit: cover; border: 1px solid rgba(0, 0, 0, 0.1);"
511
+ onerror="this.onerror=null; this.src='/static/default_avatar.png';">
512
+ </div>
513
+ <div class="flex-grow-1 ms-3">
514
+ <div class="text-wrap mb-1">${message}</div>
515
+ <small class="text-muted">${new Date(new Date(notification.created_at).getTime() + 8 * 60 * 60 * 1000).toLocaleString()}</small>
516
+ </div>
517
+ </div>
518
+ </a>
519
+ `;
520
+ notificationList.insertAdjacentHTML('beforeend', notificationHtml);
521
+ }
522
+ } catch (error) {
523
+ console.error('Error loading notifications:', error);
524
+ const notificationList = document.getElementById('notificationList');
525
+ notificationList.innerHTML = '<div class="text-center py-3 text-danger">加载消息失败</div>';
526
+ }
527
+ }
528
+
529
+ // Function to handle notification clicks
530
+ function handleNotificationClick(event, link) {
531
+ event.preventDefault();
532
+ // Close the dropdown
533
+ const dropdownMenu = document.querySelector('.dropdown-menu');
534
+ if (dropdownMenu) {
535
+ const dropdown = bootstrap.Dropdown.getInstance(document.getElementById('notificationsDropdown'));
536
+ if (dropdown) {
537
+ dropdown.hide();
538
+ }
539
+ }
540
+ // Navigate to the link
541
+ window.location.href = link;
542
+ }
543
+
544
+ // Add styles for notification items
545
+ const style = document.createElement('style');
546
+ style.textContent = `
547
+ .dropdown-item:hover {
548
+ background-color: rgba(0, 0, 0, 0.05);
549
+ }
550
+ #notificationList .dropdown-item {
551
+ white-space: normal;
552
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
553
+ }
554
+ #notificationList .dropdown-item:last-child {
555
+ border-bottom: none;
556
+ }
557
+ #notificationList .dropdown-menu {
558
+ max-height: 400px;
559
+ overflow-y: auto;
560
+ }
561
+ `;
562
+ document.head.appendChild(style);
563
+
564
+ // Update notification count every minute
565
+ setInterval(updateNotificationCount, 60000);
566
+
567
+ // Load notifications when dropdown is opened
568
+ document.getElementById('notificationsDropdown')?.addEventListener('show.bs.dropdown', loadNotifications);
569
+
570
+ // Initial load
571
+ if (document.getElementById('notificationCount')) {
572
+ updateNotificationCount();
573
+ }
574
+ </script>
575
+ {% endblock %}
576
+ </body>
577
+ </html>