2
2
{% load static %}
3
3
4
4
{% block content %}
5
- < div class ="container ">
5
+ < div class ="container " id =" mainContainer " >
6
6
<!-- 左侧面板 -->
7
7
< div class ="left-panel ">
8
8
<!-- 用户登录信息 -->
@@ -108,7 +108,7 @@ <h2 class="app-title">Swim4Love</h2>
108
108
< div class ="swimmers-section ">
109
109
< h2 > Swimmers</ h2 >
110
110
{% if have_perm %}
111
- < button class ="button " onclick =" location.href='{% url 'add_swimmer' %}'; return false; ">
111
+ < button class ="button " id =" addSwimmerBtn ">
112
112
< i class ="fa-solid fa-plus "> </ i > Add Swimmer
113
113
</ button >
114
114
{% endif %}
@@ -208,6 +208,14 @@ <h2>Swimmers</h2>
208
208
< button type ="submit " class ="delete-button "> < i class ="fa-regular fa-trash-can "> </ i >
209
209
</ button >
210
210
</ form >
211
+ {% if have_perm %}
212
+ < form action ="{% url 'toggle_favorite' swimmer.id %} " method ="post " class ="favorite-form " data-swimmer-id ="{{ swimmer.id }} ">
213
+ {% csrf_token %}
214
+ < button type ="button " class ="favorite-button {% if swimmer in current_volunteer.favorites.all %}favorited{% endif %} ">
215
+ < i class ="fa-solid fa-star "> </ i >
216
+ </ button >
217
+ </ form >
218
+ {% endif %}
211
219
</ div >
212
220
{% endif %}
213
221
</ td >
@@ -227,7 +235,7 @@ <h2>Swimmers</h2>
227
235
< h2 > Volunteers</ h2 >
228
236
< div class ="volunteer-header ">
229
237
{% if have_perm %}
230
- < button class ="button " onclick =" location.href='{% url 'add_volunteer' %}'; return false; ">
238
+ < button class ="button " id =" addVolunteerBtn ">
231
239
< i class ="fa-solid fa-plus "> </ i > Add Volunteer
232
240
</ button >
233
241
{% endif %}
@@ -249,12 +257,114 @@ <h2>Volunteers</h2>
249
257
</ div >
250
258
</ div >
251
259
</ div >
260
+
261
+ <!-- 面板切换按钮 -->
262
+ < button id ="panelToggleBtn " class ="panel-toggle " title ="切换侧栏 ">
263
+ < i class ="fa-solid fa-chevron-left "> </ i >
264
+ </ button >
252
265
</ div >
253
-
266
+
254
267
< script >
255
268
document . addEventListener ( 'DOMContentLoaded' , function ( ) {
256
- // 注意:此处事件绑定已移至real_time_leaderboard.js
257
- // 为避免重复绑定,保留此注释以便后续维护
269
+ // 添加游泳者和志愿者按钮事件
270
+ const addSwimmerBtn = document . getElementById ( 'addSwimmerBtn' ) ;
271
+ if ( addSwimmerBtn ) {
272
+ addSwimmerBtn . addEventListener ( 'click' , function ( ) {
273
+ location . href = "{% url 'add_swimmer' %}" ;
274
+ return false ;
275
+ } ) ;
276
+ }
277
+
278
+ const addVolunteerBtn = document . getElementById ( 'addVolunteerBtn' ) ;
279
+ if ( addVolunteerBtn ) {
280
+ addVolunteerBtn . addEventListener ( 'click' , function ( ) {
281
+ location . href = "{% url 'add_volunteer' %}" ;
282
+ return false ;
283
+ } ) ;
284
+ }
285
+
286
+ // Handle favorite button clicks
287
+ document . querySelectorAll ( '.favorite-button' ) . forEach ( button => {
288
+ button . addEventListener ( 'click' , function ( ) {
289
+ const form = this . closest ( '.favorite-form' ) ;
290
+ const swimmerId = form . dataset . swimmerId ;
291
+ const currentStatus = this . classList . contains ( 'favorited' ) ;
292
+
293
+ fetch ( form . action , {
294
+ method : 'POST' ,
295
+ headers : {
296
+ 'X-CSRFToken' : form . querySelector ( '[name=csrfmiddlewaretoken]' ) . value ,
297
+ 'X-Requested-With' : 'XMLHttpRequest'
298
+ } ,
299
+ credentials : 'same-origin' // 确保发送和接收cookie
300
+ } )
301
+ . then ( response => {
302
+ if ( ! response . ok ) {
303
+ throw new Error ( `请求失败: ${ response . status } ` ) ;
304
+ }
305
+ return response . json ( ) ;
306
+ } )
307
+ . then ( data => {
308
+ if ( data . success ) {
309
+ const newStatus = data . is_favorite ;
310
+
311
+ // 切换收藏状态样式(根据服务器返回的状态更新)
312
+ if ( newStatus ) {
313
+ this . classList . add ( 'favorited' ) ;
314
+ } else {
315
+ this . classList . remove ( 'favorited' ) ;
316
+ }
317
+
318
+ // 重排表格行
319
+ const tbody = document . querySelector ( '#swimmersTable tbody' ) ;
320
+ const rows = Array . from ( tbody . querySelectorAll ( 'tr' ) ) ;
321
+
322
+ rows . sort ( ( a , b ) => {
323
+ const aFavorited = a . querySelector ( '.favorite-button' ) ?. classList . contains ( 'favorited' ) || false ;
324
+ const bFavorited = b . querySelector ( '.favorite-button' ) ?. classList . contains ( 'favorited' ) || false ;
325
+ if ( aFavorited === bFavorited ) {
326
+ return a . querySelector ( 'td' ) . textContent . localeCompare ( b . querySelector ( 'td' ) . textContent ) ;
327
+ }
328
+ return bFavorited - aFavorited ;
329
+ } ) ;
330
+ rows . forEach ( row => tbody . appendChild ( row ) ) ;
331
+ }
332
+ } )
333
+ . catch ( error => {
334
+ console . error ( '请求失败:' , error ) ;
335
+ } ) ;
336
+ } ) ;
337
+ } ) ;
338
+
339
+ // 面板收缩功能
340
+ const toggleBtn = document . getElementById ( 'panelToggleBtn' ) ;
341
+ const container = document . getElementById ( 'mainContainer' ) ;
342
+
343
+ // 检查本地存储中的状态
344
+ const isPanelCollapsed = localStorage . getItem ( 'panelCollapsed' ) === 'true' ;
345
+ if ( isPanelCollapsed ) {
346
+ container . classList . add ( 'panel-collapsed' ) ;
347
+ toggleBtn . classList . add ( 'collapsed' ) ;
348
+ toggleBtn . querySelector ( 'i' ) . classList . remove ( 'fa-chevron-left' ) ;
349
+ toggleBtn . querySelector ( 'i' ) . classList . add ( 'fa-chevron-right' ) ;
350
+ }
351
+
352
+ toggleBtn . addEventListener ( 'click' , function ( ) {
353
+ container . classList . toggle ( 'panel-collapsed' ) ;
354
+ this . classList . toggle ( 'collapsed' ) ;
355
+
356
+ // 切换图标
357
+ const icon = this . querySelector ( 'i' ) ;
358
+ if ( container . classList . contains ( 'panel-collapsed' ) ) {
359
+ icon . classList . remove ( 'fa-chevron-left' ) ;
360
+ icon . classList . add ( 'fa-chevron-right' ) ;
361
+ localStorage . setItem ( 'panelCollapsed' , 'true' ) ;
362
+ } else {
363
+ icon . classList . remove ( 'fa-chevron-right' ) ;
364
+ icon . classList . add ( 'fa-chevron-left' ) ;
365
+ localStorage . setItem ( 'panelCollapsed' , 'false' ) ;
366
+ }
367
+ } ) ;
258
368
} ) ;
259
369
</ script >
260
370
{% endblock %}
0 commit comments