layout.html 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. <!DOCTYPE html>
  2. <html lang="zh-CN">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1.0">
  6. <title>{% block title %}家谱管理系统{% endblock %}</title>
  7. <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
  8. <link href="{{ url_for('static', filename='css/bootstrap-icons.min.css') }}" rel="stylesheet">
  9. <link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet">
  10. {% block extra_css %}{% endblock %}
  11. </head>
  12. <body>
  13. <div class="page-container">
  14. {% if session.get('user_id') %}
  15. <aside class="sidebar">
  16. <div class="sidebar-header">
  17. <picture>
  18. <source srcset="{{ url_for('static', filename='images/home/logo.webp') }}" type="image/webp">
  19. <img src="{{ url_for('static', filename='images/home/logo.png') }}" alt="留" class="sidebar-logo">
  20. </picture>
  21. <div class="sidebar-title">家谱管理系统</div>
  22. </div>
  23. <nav class="sidebar-nav">
  24. <a href="{{ url_for('home') }}" class="{% if request.endpoint == 'home' %}active{% endif %}">
  25. <i class="bi bi-house"></i> 系统首页
  26. </a>
  27. {% if session.get('is_super_admin') %}
  28. <a href="{{ url_for('pdf_management') }}" class="{% if request.endpoint == 'pdf_management' %}active{% endif %}">
  29. <i class="bi bi-book"></i> 家谱管理
  30. </a>
  31. {% endif %}
  32. <a href="{{ url_for('index') }}" class="{% if request.endpoint == 'index' %}active{% endif %}">
  33. <i class="bi bi-file-earmark-arrow-up"></i> 扫描件管理
  34. </a>
  35. <a href="{{ url_for('members') }}" class="{% if request.endpoint == 'members' %}active{% endif %}">
  36. <i class="bi bi-people"></i> 成员列表
  37. </a>
  38. <a href="{{ url_for('batch_genealogy') }}" class="{% if request.endpoint == 'batch_genealogy' %}active{% endif %}">
  39. <i class="bi bi-file-text"></i> 批量处理族谱原文
  40. </a>
  41. <a href="{{ url_for('tree') }}" class="{% if request.endpoint == 'tree' %}active{% endif %}">
  42. <i class="bi bi-diagram-3"></i> 家谱世系树状图
  43. </a>
  44. <a href="{{ url_for('tree_gen') }}" class="{% if request.endpoint == 'tree_gen' %}active{% endif %}">
  45. <i class="bi bi-layout-three-columns"></i> 世代分层树
  46. </a>
  47. <a href="{{ url_for('lineage_query') }}" class="{% if request.endpoint == 'lineage_query' %}active{% endif %}">
  48. <i class="bi bi-tree"></i> 世系查询
  49. </a>
  50. <a href="{{ url_for('settlements') }}" class="{% if request.endpoint == 'settlements' %}active{% endif %}">
  51. <i class="bi bi-globe"></i> 聚落地图
  52. </a>
  53. <a href="{{ url_for('generation_check_page') }}" class="{% if request.endpoint == 'generation_check_page' %}active{% endif %}">
  54. <i class="bi bi-clipboard2-check"></i> 代数核查
  55. </a>
  56. <a href="{{ url_for('suspected_errors') }}" class="{% if request.endpoint == 'suspected_errors' %}active{% endif %}">
  57. <i class="bi bi-exclamation-triangle"></i> 疑似错误汇总
  58. </a>
  59. </nav>
  60. <div class="sidebar-footer">
  61. <div class="sidebar-user">
  62. <div class="sidebar-user-icon">{{ session['username'][0] if session['username'] else '用' }}</div>
  63. <span class="sidebar-user-name">{{ session['username'] }}</span>
  64. </div>
  65. <a href="{{ url_for('logout') }}" class="sidebar-logout">
  66. <i class="bi bi-box-arrow-right"></i> 退出登录
  67. </a>
  68. </div>
  69. </aside>
  70. {% endif %}
  71. <div class="watermark-container" id="watermarkContainer"></div>
  72. <div class="watermark-corner" id="watermarkCorner"></div>
  73. <main class="main-content">
  74. {% with messages = get_flashed_messages(with_categories=true) %}
  75. {% if messages %}
  76. {% for category, message in messages %}
  77. {% if category == 'warning' %}
  78. <div class="alert alert-warning alert-dismissible fade show mb-4" role="alert">
  79. <i class="bi bi-exclamation-triangle me-2"></i>{{ message }}
  80. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  81. </div>
  82. {% elif category == 'error' %}
  83. <div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
  84. <i class="bi bi-x-circle me-2"></i>{{ message }}
  85. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  86. </div>
  87. {% else %}
  88. <div class="alert alert-info alert-dismissible fade show mb-4" role="alert">
  89. <i class="bi bi-info-circle me-2"></i>{{ message }}
  90. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  91. </div>
  92. {% endif %}
  93. {% endfor %}
  94. {% endif %}
  95. {% endwith %}
  96. {% block content %}{% endblock %}
  97. </main>
  98. </div>
  99. <script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
  100. <script>
  101. document.addEventListener('DOMContentLoaded', function() {
  102. const username = "{{ session.get('username', '') }}";
  103. const container = document.getElementById('watermarkContainer');
  104. const corner = document.getElementById('watermarkCorner');
  105. function formatWatermarkText() {
  106. if (!username) return '';
  107. const now = new Date();
  108. const year = now.getFullYear();
  109. const month = String(now.getMonth() + 1).padStart(2, '0');
  110. const day = String(now.getDate()).padStart(2, '0');
  111. const hour = String(now.getHours()).padStart(2, '0');
  112. const minute = String(now.getMinutes()).padStart(2, '0');
  113. return `${username}_${year}${month}${day}_${hour}${minute}`;
  114. }
  115. if (container && username) {
  116. const cols = 6;
  117. const rows = 8;
  118. const cellWidth = window.innerWidth / cols;
  119. const cellHeight = window.innerHeight / rows;
  120. function updateTiledWatermark() {
  121. container.innerHTML = '';
  122. const watermarkText = formatWatermarkText();
  123. for (let i = 0; i < rows; i++) {
  124. for (let j = 0; j < cols; j++) {
  125. const span = document.createElement('span');
  126. span.className = 'watermark-text';
  127. span.textContent = watermarkText;
  128. span.style.left = (j * cellWidth + 20) + 'px';
  129. span.style.top = (i * cellHeight + 20) + 'px';
  130. container.appendChild(span);
  131. }
  132. }
  133. }
  134. updateTiledWatermark();
  135. setInterval(updateTiledWatermark, 60000);
  136. }
  137. function updateCornerWatermark() {
  138. if (corner && username) {
  139. corner.textContent = formatWatermarkText();
  140. }
  141. }
  142. updateCornerWatermark();
  143. setInterval(updateCornerWatermark, 1000);
  144. });
  145. </script>
  146. {% block extra_js %}{% endblock %}
  147. </body>
  148. </html>