layout.html 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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. <!-- Local Bootstrap CSS -->
  8. <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
  9. <!-- Local Bootstrap Icons CSS -->
  10. <link href="{{ url_for('static', filename='css/bootstrap-icons.min.css') }}" rel="stylesheet">
  11. <style>
  12. body { font-family: 'Microsoft YaHei', sans-serif; }
  13. .sidebar { min-height: 100vh; background-color: #343a40; color: white; }
  14. .sidebar a { color: rgba(255,255,255,.8); text-decoration: none; padding: 10px 20px; display: block; }
  15. .sidebar a:hover { background-color: #495057; color: white; }
  16. .sidebar a.active { background-color: #0d6efd; color: white; }
  17. .content-area { padding: 20px; }
  18. /* Watermark styles */
  19. .watermark-container {
  20. position: fixed;
  21. top: 0;
  22. left: 0;
  23. width: 100%;
  24. height: 100%;
  25. pointer-events: none;
  26. z-index: 9999;
  27. overflow: hidden;
  28. opacity: 0.08;
  29. }
  30. .watermark-text {
  31. position: absolute;
  32. font-size: 18px;
  33. color: #999;
  34. transform: rotate(-15deg);
  35. white-space: nowrap;
  36. font-weight: 500;
  37. letter-spacing: 2px;
  38. }
  39. .watermark-corner {
  40. position: fixed;
  41. bottom: 15px;
  42. right: 15px;
  43. font-size: 12px;
  44. color: #999;
  45. opacity: 0.4;
  46. text-align: right;
  47. z-index: 1000;
  48. pointer-events: none;
  49. }
  50. </style>
  51. {% block extra_css %}{% endblock %}
  52. </head>
  53. <body class="bg-light">
  54. <div class="container-fluid">
  55. <div class="row">
  56. {% if session.get('user_id') %}
  57. <!-- Sidebar -->
  58. <div class="col-md-2 sidebar d-none d-md-block">
  59. <div class="py-4 text-center border-bottom mb-4">
  60. <h4>家谱管理</h4>
  61. </div>
  62. <nav>
  63. <a href="{{ url_for('home') }}" class="{% if request.endpoint == 'home' %}active{% endif %}">
  64. <i class="bi bi-house me-2"></i> 系统首页
  65. </a>
  66. {% if session.get('is_super_admin') %}
  67. <a href="{{ url_for('pdf_management') }}" class="{% if request.endpoint == 'pdf_management' %}active{% endif %}">
  68. <i class="bi bi-book me-2"></i> 家谱管理
  69. </a>
  70. {% endif %}
  71. <a href="{{ url_for('index') }}" class="{% if request.endpoint == 'index' %}active{% endif %}">
  72. <i class="bi bi-file-earmark-arrow-up me-2"></i> 扫描件管理
  73. </a>
  74. <a href="{{ url_for('members') }}" class="{% if request.endpoint == 'members' %}active{% endif %}">
  75. <i class="bi bi-people me-2"></i> 成员列表
  76. </a>
  77. <a href="{{ url_for('batch_genealogy') }}" class="{% if request.endpoint == 'batch_genealogy' %}active{% endif %}">
  78. <i class="bi bi-file-text me-2"></i> 批量处理族谱原文
  79. </a>
  80. <a href="{{ url_for('tree') }}" class="{% if request.endpoint == 'tree' %}active{% endif %}">
  81. <i class="bi bi-diagram-3 me-2"></i> 家谱世系树状图
  82. </a>
  83. <a href="{{ url_for('tree_gen') }}" class="{% if request.endpoint == 'tree_gen' %}active{% endif %}">
  84. <i class="bi bi-layout-three-columns me-2"></i> 世代分层树
  85. </a>
  86. <a href="{{ url_for('lineage_query') }}" class="{% if request.endpoint == 'lineage_query' %}active{% endif %}">
  87. <i class="bi bi-tree me-2"></i> 世系查询
  88. </a>
  89. <a href="{{ url_for('settlements') }}" class="{% if request.endpoint == 'settlements' %}active{% endif %}">
  90. <i class="bi bi-globe me-2"></i> 聚落地图
  91. </a>
  92. <a href="{{ url_for('generation_check_page') }}" class="{% if request.endpoint == 'generation_check_page' %}active{% endif %}">
  93. <i class="bi bi-clipboard2-check me-2"></i> 代数核查
  94. </a>
  95. <div class="mt-5 border-top pt-3">
  96. <p class="px-3 small text-muted">用户: {{ session['username'] }}</p>
  97. <a href="{{ url_for('logout') }}" class="text-danger">
  98. <i class="bi bi-box-arrow-right me-2"></i> 退出登录
  99. </a>
  100. </div>
  101. </nav>
  102. </div>
  103. {% endif %}
  104. <!-- Watermark -->
  105. <div class="watermark-container" id="watermarkContainer"></div>
  106. <div class="watermark-corner" id="watermarkCorner"></div>
  107. <!-- Main Content -->
  108. <div class="col-md-{% if session.get('user_id') %}10{% else %}12{% endif %} content-area">
  109. {% with messages = get_flashed_messages(with_categories=true) %}
  110. {% if messages %}
  111. {% for category, message in messages %}
  112. {% if category == 'warning' %}
  113. <div class="alert alert-warning alert-dismissible fade show" role="alert">
  114. {{ message }}
  115. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  116. </div>
  117. {% elif category == 'error' %}
  118. <div class="alert alert-danger alert-dismissible fade show" role="alert">
  119. {{ message }}
  120. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  121. </div>
  122. {% else %}
  123. <div class="alert alert-info alert-dismissible fade show" role="alert">
  124. {{ message }}
  125. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  126. </div>
  127. {% endif %}
  128. {% endfor %}
  129. {% endif %}
  130. {% endwith %}
  131. {% block content %}{% endblock %}
  132. </div>
  133. </div>
  134. </div>
  135. <!-- Local Bootstrap JS -->
  136. <script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
  137. <!-- Watermark Script -->
  138. <script>
  139. document.addEventListener('DOMContentLoaded', function() {
  140. const username = "{{ session.get('username', '') }}";
  141. const container = document.getElementById('watermarkContainer');
  142. const corner = document.getElementById('watermarkCorner');
  143. function formatWatermarkText() {
  144. if (!username) return '';
  145. const now = new Date();
  146. const year = now.getFullYear();
  147. const month = String(now.getMonth() + 1).padStart(2, '0');
  148. const day = String(now.getDate()).padStart(2, '0');
  149. const hour = String(now.getHours()).padStart(2, '0');
  150. const minute = String(now.getMinutes()).padStart(2, '0');
  151. return `${username}_${year}${month}${day}_${hour}${minute}`;
  152. }
  153. // Generate tiled watermark
  154. if (container && username) {
  155. const cols = 6;
  156. const rows = 8;
  157. const cellWidth = window.innerWidth / cols;
  158. const cellHeight = window.innerHeight / rows;
  159. function updateTiledWatermark() {
  160. container.innerHTML = '';
  161. const watermarkText = formatWatermarkText();
  162. for (let i = 0; i < rows; i++) {
  163. for (let j = 0; j < cols; j++) {
  164. const span = document.createElement('span');
  165. span.className = 'watermark-text';
  166. span.textContent = watermarkText;
  167. span.style.left = (j * cellWidth + 20) + 'px';
  168. span.style.top = (i * cellHeight + 20) + 'px';
  169. container.appendChild(span);
  170. }
  171. }
  172. }
  173. updateTiledWatermark();
  174. setInterval(updateTiledWatermark, 60000);
  175. }
  176. // Update corner watermark
  177. function updateCornerWatermark() {
  178. if (corner && username) {
  179. corner.textContent = formatWatermarkText();
  180. }
  181. }
  182. updateCornerWatermark();
  183. setInterval(updateCornerWatermark, 1000);
  184. });
  185. </script>
  186. {% block extra_js %}{% endblock %}
  187. </body>
  188. </html>