林海 пре 12 часа
родитељ
комит
e43f09e71c

+ 777 - 0
static/css/main.css

@@ -0,0 +1,777 @@
+@font-face {
+    font-family: "HuakangWeibei";
+    src: url("../fonts/home/HuakangWeibei.woff2") format("woff2");
+    font-weight: normal;
+    font-style: normal;
+    font-display: swap;
+}
+@font-face {
+    font-family: "AdobeKaiti";
+    src: url("../fonts/home/AdobeKaitiStd-Regular.woff2") format("woff2");
+    font-weight: normal;
+    font-style: normal;
+    font-display: swap;
+}
+@font-face {
+    font-family: "SourceHanSerif";
+    src: url("../fonts/home/SourceHanSerifCN-Regular.woff2") format("woff2");
+    font-weight: normal;
+    font-style: normal;
+    font-display: swap;
+}
+
+:root {
+    --color-primary: #8B6914;
+    --color-primary-dark: #6B4C12;
+    --color-primary-light: #A68A37;
+    --color-secondary: #2C1810;
+    --color-accent: #D4AF37;
+    --color-accent-light: #F0E68C;
+    --color-bg-main: #FDFBF7;
+    --color-bg-card: #FFFFFF;
+    --color-bg-sidebar: #2C1810;
+    --color-bg-sidebar-hover: #3D2417;
+    --color-text-primary: #2C1810;
+    --color-text-secondary: #5C4033;
+    --color-text-muted: #8B7355;
+    --color-text-sidebar: #D4C4A8;
+    --color-text-sidebar-active: #FDFBF7;
+    --color-border: #E8DFD4;
+    --color-border-light: #F0E6DC;
+    --color-success: #5D8A4B;
+    --color-warning: #C9A83D;
+    --color-danger: #A84B4B;
+    --color-info: #4B7AA8;
+    --font-family-primary: "AdobeKaiti", "KaiTi", "STKaiti", serif;
+    --font-family-heading: "HuakangWeibei", "STSong", "SimSun", serif;
+    --font-family-body: "SourceHanSerif", "Noto Serif SC", "Songti SC", serif;
+    --radius-sm: 6px;
+    --radius-md: 10px;
+    --radius-lg: 16px;
+    --radius-xl: 20px;
+    --shadow-sm: 0 2px 8px rgba(44, 24, 16, 0.06);
+    --shadow-md: 0 4px 16px rgba(44, 24, 16, 0.08);
+    --shadow-lg: 0 8px 24px rgba(44, 24, 16, 0.12);
+    --shadow-card: 0 4px 12px rgba(44, 24, 16, 0.06);
+    --shadow-card-hover: 0 8px 20px rgba(44, 24, 16, 0.1);
+    --transition-fast: 0.15s ease;
+    --transition-normal: 0.25s ease;
+    --transition-slow: 0.35s ease;
+    --spacing-xs: 4px;
+    --spacing-sm: 8px;
+    --spacing-md: 16px;
+    --spacing-lg: 24px;
+    --spacing-xl: 32px;
+    --spacing-xxl: 48px;
+}
+
+*, *::before, *::after {
+    box-sizing: border-box;
+    margin: 0;
+    padding: 0;
+}
+
+html {
+    font-size: 15px;
+    scroll-behavior: smooth;
+}
+
+body {
+    font-family: var(--font-family-body);
+    background-color: var(--color-bg-main);
+    color: var(--color-text-primary);
+    line-height: 1.6;
+    min-height: 100vh;
+    overflow-x: hidden;
+}
+
+::-webkit-scrollbar {
+    width: 8px;
+    height: 8px;
+}
+
+::-webkit-scrollbar-track {
+    background: var(--color-bg-main);
+}
+
+::-webkit-scrollbar-thumb {
+    background: var(--color-border);
+    border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+    background: var(--color-text-muted);
+}
+
+.page-container {
+    display: flex;
+    min-height: 100vh;
+}
+
+.sidebar {
+    width: 260px;
+    min-height: 100vh;
+    background: var(--color-bg-sidebar);
+    color: var(--color-text-sidebar);
+    position: fixed;
+    left: 0;
+    top: 0;
+    z-index: 1000;
+    display: flex;
+    flex-direction: column;
+    box-shadow: 4px 0 16px rgba(0, 0, 0, 0.15);
+    transition: transform 0.3s ease;
+}
+
+.sidebar-header {
+    padding: var(--spacing-xl) var(--spacing-lg);
+    border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+    text-align: center;
+}
+
+.sidebar-logo {
+    width: 60px;
+    height: auto;
+    margin-bottom: var(--spacing-sm);
+}
+
+.sidebar-title {
+    font-family: var(--font-family-heading);
+    font-size: 1.1rem;
+    color: var(--color-accent);
+    letter-spacing: 0.15em;
+}
+
+.sidebar-nav {
+    flex: 1;
+    padding: var(--spacing-md);
+    padding-bottom: 120px;
+}
+
+.sidebar-nav a {
+    display: flex;
+    align-items: center;
+    gap: var(--spacing-md);
+    padding: var(--spacing-md) var(--spacing-lg);
+    color: var(--color-text-sidebar);
+    text-decoration: none;
+    font-size: 0.95rem;
+    border-radius: var(--radius-md);
+    transition: all var(--transition-fast);
+    margin-bottom: 2px;
+    font-family: var(--font-family-primary);
+}
+
+.sidebar-nav a:hover {
+    background: var(--color-bg-sidebar-hover);
+    color: var(--color-text-sidebar-active);
+    transform: translateX(4px);
+}
+
+.sidebar-nav a.active {
+    background: linear-gradient(135deg, rgba(139, 105, 20, 0.3), rgba(212, 175, 55, 0.1));
+    color: var(--color-accent);
+    border-left: 3px solid var(--color-accent);
+}
+
+.sidebar-nav a i {
+    width: 20px;
+    font-size: 1rem;
+    text-align: center;
+}
+
+.sidebar-footer {
+    padding: var(--spacing-md) var(--spacing-md) var(--spacing-xl);
+    border-top: 1px solid rgba(255, 255, 255, 0.1);
+    background: rgba(0, 0, 0, 0.1);
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    right: 0;
+}
+
+.sidebar-user {
+    display: flex;
+    align-items: center;
+    gap: var(--spacing-md);
+    padding: var(--spacing-sm) var(--spacing-md);
+    border-radius: var(--radius-md);
+    background: rgba(255, 255, 255, 0.04);
+    margin-bottom: var(--spacing-md);
+}
+
+.sidebar-user-icon {
+    width: 36px;
+    height: 36px;
+    border-radius: 50%;
+    background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: white;
+    font-size: 0.9rem;
+    font-weight: 600;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
+}
+
+.sidebar-user-name {
+    font-size: 0.85rem;
+    color: var(--color-text-sidebar-active);
+    font-family: var(--font-family-primary);
+}
+
+.sidebar-logout {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: var(--spacing-sm);
+    padding: var(--spacing-sm) var(--spacing-md);
+    color: #E8D4D4;
+    text-decoration: none;
+    font-size: 0.85rem;
+    border-radius: var(--radius-md);
+    transition: all var(--transition-fast);
+    background: rgba(168, 75, 75, 0.1);
+    border: 1px solid rgba(168, 75, 75, 0.2);
+}
+
+.sidebar-logout:hover {
+    background: rgba(168, 75, 75, 0.25);
+    color: #FFCCCC;
+    border-color: rgba(168, 75, 75, 0.4);
+    transform: translateY(-1px);
+}
+
+.sidebar-logout i {
+    font-size: 0.85rem;
+}
+
+.main-content {
+    margin-left: 260px;
+    flex: 1;
+    min-height: 100vh;
+    padding: var(--spacing-xl);
+    background: var(--color-bg-main);
+}
+
+.page-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: var(--spacing-xl);
+    flex-wrap: wrap;
+    gap: var(--spacing-md);
+}
+
+.page-title {
+    display: flex;
+    align-items: center;
+    gap: var(--spacing-md);
+}
+
+.page-title-icon {
+    width: 44px;
+    height: 44px;
+    border-radius: var(--radius-lg);
+    background: linear-gradient(135deg, rgba(139, 105, 20, 0.15), rgba(212, 175, 55, 0.1));
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: var(--color-primary);
+    font-size: 1.25rem;
+}
+
+.page-title-text {
+    font-family: var(--font-family-heading);
+    font-size: 1.5rem;
+    color: var(--color-secondary);
+    letter-spacing: 0.05em;
+}
+
+.page-actions {
+    display: flex;
+    gap: var(--spacing-sm);
+    flex-wrap: wrap;
+}
+
+.card {
+    background: var(--color-bg-card);
+    border: none;
+    border-radius: var(--radius-lg);
+    box-shadow: var(--shadow-card);
+    transition: all var(--transition-normal);
+    overflow: hidden;
+}
+
+.card:hover {
+    box-shadow: var(--shadow-card-hover);
+}
+
+.card-header {
+    background: linear-gradient(135deg, rgba(139, 105, 20, 0.08), rgba(212, 175, 55, 0.04));
+    border-bottom: 1px solid var(--color-border-light);
+    padding: var(--spacing-lg);
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+}
+
+.card-header-title {
+    font-family: var(--font-family-heading);
+    font-size: 1.1rem;
+    color: var(--color-secondary);
+    margin: 0;
+}
+
+.card-body {
+    padding: var(--spacing-lg);
+}
+
+.btn {
+    display: inline-flex;
+    align-items: center;
+    gap: var(--spacing-xs);
+    padding: var(--spacing-sm) var(--spacing-lg);
+    font-family: var(--font-family-primary);
+    font-size: 0.9rem;
+    border-radius: var(--radius-md);
+    transition: all var(--transition-fast);
+    border: none;
+    cursor: pointer;
+    text-decoration: none;
+    white-space: nowrap;
+}
+
+.btn-primary {
+    background: linear-gradient(135deg, var(--color-primary), var(--color-primary-light));
+    color: white;
+    box-shadow: 0 2px 8px rgba(139, 105, 20, 0.3);
+}
+
+.btn-primary:hover {
+    background: linear-gradient(135deg, var(--color-primary-dark), var(--color-primary));
+    transform: translateY(-1px);
+    box-shadow: 0 4px 12px rgba(139, 105, 20, 0.4);
+}
+
+.btn-secondary {
+    background: var(--color-bg-card);
+    color: var(--color-text-secondary);
+    border: 1px solid var(--color-border);
+}
+
+.btn-secondary:hover {
+    background: var(--color-bg-main);
+    border-color: var(--color-primary);
+    color: var(--color-primary);
+}
+
+.btn-outline-primary {
+    background: transparent;
+    color: var(--color-primary);
+    border: 1px solid var(--color-primary);
+}
+
+.btn-outline-primary:hover {
+    background: var(--color-primary);
+    color: white;
+}
+
+.btn-success {
+    background: linear-gradient(135deg, var(--color-success), #6D9A5B);
+    color: white;
+}
+
+.btn-success:hover {
+    background: linear-gradient(135deg, #4D7A3B, var(--color-success));
+}
+
+.btn-warning {
+    background: linear-gradient(135deg, var(--color-warning), #D9B84D);
+    color: var(--color-secondary);
+}
+
+.btn-warning:hover {
+    background: linear-gradient(135deg, #B9982D, var(--color-warning));
+}
+
+.btn-danger {
+    background: linear-gradient(135deg, var(--color-danger), #B85B5B);
+    color: white;
+}
+
+.btn-danger:hover {
+    background: linear-gradient(135deg, #983B3B, var(--color-danger));
+}
+
+.btn-info {
+    background: linear-gradient(135deg, var(--color-info), #5B8AA8);
+    color: white;
+}
+
+.btn-info:hover {
+    background: linear-gradient(135deg, #3B6A98, var(--color-info));
+}
+
+.btn-sm {
+    padding: var(--spacing-xs) var(--spacing-md);
+    font-size: 0.8rem;
+}
+
+.badge {
+    display: inline-flex;
+    align-items: center;
+    gap: var(--spacing-xs);
+    padding: 2px 10px;
+    border-radius: 20px;
+    font-size: 0.75rem;
+    font-weight: 500;
+    font-family: var(--font-family-primary);
+}
+
+.badge-success {
+    background: rgba(93, 138, 75, 0.15);
+    color: var(--color-success);
+    border: 1px solid rgba(93, 138, 75, 0.2);
+}
+
+.badge-warning {
+    background: rgba(201, 168, 61, 0.15);
+    color: var(--color-warning);
+    border: 1px solid rgba(201, 168, 61, 0.2);
+}
+
+.badge-danger {
+    background: rgba(168, 75, 75, 0.15);
+    color: var(--color-danger);
+    border: 1px solid rgba(168, 75, 75, 0.2);
+}
+
+.badge-info {
+    background: rgba(75, 122, 168, 0.15);
+    color: var(--color-info);
+    border: 1px solid rgba(75, 122, 168, 0.2);
+}
+
+.badge-primary {
+    background: rgba(139, 105, 20, 0.15);
+    color: var(--color-primary);
+    border: 1px solid rgba(139, 105, 20, 0.2);
+}
+
+.badge-secondary {
+    background: rgba(139, 115, 85, 0.15);
+    color: var(--color-text-muted);
+    border: 1px solid rgba(139, 115, 85, 0.2);
+}
+
+.form-control, .form-select {
+    font-family: var(--font-family-body);
+    font-size: 0.9rem;
+    border: 1px solid var(--color-border);
+    border-radius: var(--radius-md);
+    padding: var(--spacing-sm) var(--spacing-md);
+    background: var(--color-bg-card);
+    color: var(--color-text-primary);
+    transition: all var(--transition-fast);
+}
+
+.form-control:focus, .form-select:focus {
+    outline: none;
+    border-color: var(--color-primary);
+    box-shadow: 0 0 0 3px rgba(139, 105, 20, 0.1);
+}
+
+.form-label {
+    font-family: var(--font-family-primary);
+    font-size: 0.85rem;
+    color: var(--color-text-secondary);
+    font-weight: 500;
+    margin-bottom: var(--spacing-xs);
+}
+
+.table {
+    width: 100%;
+    border-collapse: separate;
+    border-spacing: 0;
+}
+
+.table thead th {
+    background: linear-gradient(135deg, rgba(139, 105, 20, 0.06), rgba(212, 175, 55, 0.03));
+    border-bottom: 2px solid var(--color-border);
+    padding: var(--spacing-md);
+    font-family: var(--font-family-heading);
+    font-size: 0.85rem;
+    color: var(--color-text-secondary);
+    text-align: left;
+    letter-spacing: 0.05em;
+    vertical-align: middle;
+}
+
+.table tbody td {
+    padding: var(--spacing-md);
+    border-bottom: 1px solid var(--color-border-light);
+    font-size: 0.85rem;
+    color: var(--color-text-primary);
+    vertical-align: middle;
+}
+
+.table tbody tr:hover {
+    background: rgba(139, 105, 20, 0.03);
+}
+
+.table tbody tr:last-child td {
+    border-bottom: none;
+}
+
+.pagination {
+    display: flex;
+    gap: 2px;
+    justify-content: center;
+}
+
+.pagination .page-item .page-link {
+    padding: var(--spacing-sm) var(--spacing-md);
+    font-size: 0.85rem;
+    border-radius: var(--radius-sm);
+    color: var(--color-text-secondary);
+    border: none;
+    background: transparent;
+    transition: all var(--transition-fast);
+    font-family: var(--font-family-primary);
+}
+
+.pagination .page-item.active .page-link {
+    background: var(--color-primary);
+    color: white;
+}
+
+.pagination .page-item:hover:not(.active) .page-link {
+    background: rgba(139, 105, 20, 0.1);
+    color: var(--color-primary);
+}
+
+.pagination .page-item.disabled .page-link {
+    color: var(--color-text-muted);
+    cursor: not-allowed;
+}
+
+.alert {
+    border-radius: var(--radius-md);
+    padding: var(--spacing-md) var(--spacing-lg);
+    border: none;
+    font-family: var(--font-family-primary);
+}
+
+.alert-info {
+    background: rgba(75, 122, 168, 0.1);
+    color: var(--color-info);
+}
+
+.alert-warning {
+    background: rgba(201, 168, 61, 0.1);
+    color: var(--color-warning);
+}
+
+.alert-danger {
+    background: rgba(168, 75, 75, 0.1);
+    color: var(--color-danger);
+}
+
+.nav-tabs {
+    border-bottom: 2px solid var(--color-border);
+}
+
+.nav-tabs .nav-link {
+    font-family: var(--font-family-primary);
+    font-size: 0.9rem;
+    color: var(--color-text-muted);
+    border: none;
+    border-radius: 0;
+    padding: var(--spacing-sm) var(--spacing-lg);
+    margin-right: var(--spacing-md);
+    transition: all var(--transition-fast);
+    position: relative;
+}
+
+.nav-tabs .nav-link:hover {
+    color: var(--color-primary);
+}
+
+.nav-tabs .nav-link.active {
+    color: var(--color-primary);
+    background: transparent;
+}
+
+.nav-tabs .nav-link.active::after {
+    content: '';
+    position: absolute;
+    bottom: -2px;
+    left: 0;
+    right: 0;
+    height: 2px;
+    background: var(--color-primary);
+}
+
+.section-title {
+    font-family: var(--font-family-heading);
+    font-size: 1rem;
+    color: var(--color-secondary);
+    padding-left: var(--spacing-md);
+    border-left: 4px solid var(--color-primary);
+    margin: var(--spacing-xl) 0 var(--spacing-lg);
+    letter-spacing: 0.05em;
+}
+
+.info-row {
+    display: flex;
+    margin-bottom: var(--spacing-md);
+}
+
+.info-label {
+    width: 120px;
+    font-family: var(--font-family-primary);
+    font-size: 0.85rem;
+    color: var(--color-text-muted);
+    flex-shrink: 0;
+}
+
+.info-value {
+    font-family: var(--font-family-body);
+    font-size: 0.85rem;
+    color: var(--color-text-primary);
+    flex: 1;
+}
+
+.lineage-badge {
+    display: inline-flex;
+    align-items: center;
+    gap: var(--spacing-xs);
+    padding: 4px 12px;
+    border: 1px solid rgba(139, 105, 20, 0.2);
+    border-radius: 20px;
+    background: rgba(139, 105, 20, 0.08);
+    color: var(--color-primary);
+    font-size: 0.75rem;
+    font-family: var(--font-family-primary);
+    white-space: nowrap;
+}
+
+.relation-card {
+    border-left: 4px solid var(--color-primary);
+    background: rgba(139, 105, 20, 0.04);
+    padding: var(--spacing-md);
+    margin-bottom: var(--spacing-sm);
+    border-radius: 0 var(--radius-md) var(--radius-md) 0;
+}
+
+.watermark-container {
+    position: fixed;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    pointer-events: none;
+    z-index: 9999;
+    overflow: hidden;
+    opacity: 0.06;
+}
+
+.watermark-text {
+    position: absolute;
+    font-family: var(--font-family-heading);
+    font-size: 16px;
+    color: var(--color-text-muted);
+    transform: rotate(-15deg);
+    white-space: nowrap;
+    letter-spacing: 2px;
+}
+
+.watermark-corner {
+    position: fixed;
+    bottom: 15px;
+    right: 15px;
+    font-size: 11px;
+    color: var(--color-text-muted);
+    opacity: 0.4;
+    text-align: right;
+    z-index: 1000;
+    pointer-events: none;
+    font-family: var(--font-family-primary);
+}
+
+@media (max-width: 992px) {
+    .sidebar {
+        transform: translateX(-100%);
+        width: 240px;
+    }
+    
+    .sidebar.show {
+        transform: translateX(0);
+    }
+    
+    .main-content {
+        margin-left: 0;
+        padding: var(--spacing-lg);
+    }
+    
+    .page-header {
+        flex-direction: column;
+        align-items: flex-start;
+    }
+    
+    .page-actions {
+        width: 100%;
+        flex-wrap: wrap;
+    }
+    
+    .info-row {
+        flex-direction: column;
+    }
+    
+    .info-label {
+        width: auto;
+        margin-bottom: 2px;
+    }
+}
+
+@media (max-width: 576px) {
+    .main-content {
+        padding: var(--spacing-md);
+    }
+    
+    .card-body {
+        padding: var(--spacing-md);
+    }
+    
+    .table thead {
+        display: none;
+    }
+    
+    .table tbody tr {
+        display: block;
+        margin-bottom: var(--spacing-md);
+        border: 1px solid var(--color-border);
+        border-radius: var(--radius-md);
+    }
+    
+    .table tbody td {
+        display: block;
+        text-align: right;
+        padding: var(--spacing-sm) var(--spacing-md);
+        position: relative;
+        border-bottom: 1px dashed var(--color-border-light);
+    }
+    
+    .table tbody td::before {
+        content: attr(data-label);
+        position: absolute;
+        left: var(--spacing-md);
+        font-weight: 500;
+        color: var(--color-text-muted);
+    }
+    
+    .table tbody td:last-child {
+        border-bottom: none;
+    }
+}

Разлика између датотеке није приказан због своје велике величине
+ 481 - 360
templates/add_member.html


+ 204 - 129
templates/index.html

@@ -5,50 +5,108 @@
 {% block extra_css %}
 <style>
     .thumbnail-img {
-        width: 50px;
-        height: 70px;
+        width: 60px;
+        height: 80px;
         object-fit: cover;
-        border-radius: 4px;
+        border-radius: var(--radius-sm);
         cursor: pointer;
-        border: 1px solid #dee2e6;
-        transition: transform 0.2s;
-        background-color: #f8f9fa;
+        border: 1px solid var(--color-border);
+        transition: all var(--transition-fast);
+        background-color: var(--color-bg-main);
     }
+    
     .thumbnail-img:hover {
-        transform: scale(1.5);
-        box-shadow: 0 4px 8px rgba(0,0,0,0.15);
+        transform: scale(1.1);
+        box-shadow: var(--shadow-md);
         z-index: 10;
         position: relative;
     }
-
-    /* Modal Image Viewer Styles */
+    
+    .file-type-badge {
+        font-size: 0.7rem;
+        padding: 2px 8px;
+        border-radius: 4px;
+    }
+    
+    .file-type-badge.pdf {
+        background: rgba(168, 75, 75, 0.15);
+        color: var(--color-danger);
+        border: 1px solid rgba(168, 75, 75, 0.2);
+    }
+    
+    .file-type-badge.image {
+        background: rgba(93, 138, 75, 0.15);
+        color: var(--color-success);
+        border: 1px solid rgba(93, 138, 75, 0.2);
+    }
+    
+    .table-action-btns {
+        display: flex;
+        gap: var(--spacing-xs);
+        justify-content: center;
+        flex-wrap: wrap;
+    }
+    
+    .table-action-btns .btn {
+        padding: 4px 10px;
+        font-size: 0.75rem;
+    }
+    
+    .empty-state {
+        text-align: center;
+        padding: var(--spacing-xxl);
+        color: var(--color-text-muted);
+    }
+    
+    .empty-state i {
+        font-size: 3rem;
+        margin-bottom: var(--spacing-md);
+        opacity: 0.5;
+    }
+    
+    .search-form {
+        display: flex;
+        flex-wrap: wrap;
+        gap: var(--spacing-md);
+        align-items: flex-end;
+    }
+    
+    .search-form .form-group {
+        flex: 1;
+        min-width: 150px;
+    }
+    
     .modal-image-container {
         height: 85vh;
         display: flex;
         flex-direction: column;
     }
+    
     .image-toolbar {
-        background: #e9ecef;
-        padding: 5px 10px;
-        border-bottom: 1px solid #dee2e6;
+        background: var(--color-bg-card);
+        padding: var(--spacing-sm) var(--spacing-md);
+        border-bottom: 1px solid var(--color-border);
         display: flex;
-        gap: 10px;
+        gap: var(--spacing-md);
         align-items: center;
         flex-wrap: wrap;
     }
+    
     .image-viewer { 
         flex: 1; 
-        border: 1px solid #ccc; 
-        background: #f0f0f0; 
+        border: 1px solid var(--color-border); 
+        background: var(--color-bg-main); 
         overflow: hidden; 
         text-align: center; 
         position: relative; 
         cursor: grab;
         user-select: none;
     }
+    
     .image-viewer:active {
         cursor: grabbing;
     }
+    
     .image-wrapper {
         display: inline-block;
         transition: transform 0.2s ease-out;
@@ -58,6 +116,7 @@
         left: 50%;
         transform: translate(-50%, -50%);
     }
+    
     .image-wrapper img {
         max-width: 100%;
         max-height: 100%;
@@ -65,16 +124,17 @@
         pointer-events: none;
         user-select: none;
         transition: filter 0.2s;
-        box-shadow: 0 0 20px rgba(0,0,0,0.1);
+        box-shadow: var(--shadow-md);
     }
+    
     .magnifier-glass {
         position: absolute;
-        border: 3px solid #000;
+        border: 3px solid var(--color-primary);
         border-radius: 50%;
         cursor: none;
         width: 150px;
         height: 150px;
-        box-shadow: 0 0 10px rgba(0,0,0,0.5);
+        box-shadow: var(--shadow-lg);
         display: none;
         z-index: 1000;
         background-repeat: no-repeat;
@@ -85,29 +145,42 @@
 {% endblock %}
 
 {% block content %}
-<div class="d-flex justify-content-between align-items-center mb-4">
-    <h2><i class="bi bi-file-earmark-arrow-up"></i> 扫描件管理</h2>
-    <a href="{{ url_for('upload') }}" class="btn btn-primary">
-        <i class="bi bi-cloud-upload me-2"></i> 上传扫描件
-    </a>
+<div class="page-header">
+    <div class="page-title">
+        <div class="page-title-icon">
+            <i class="bi bi-file-earmark-arrow-up"></i>
+        </div>
+        <div>
+            <h1 class="page-title-text">扫描件管理</h1>
+            <p class="member-meta mt-1">管理上传的家谱扫描件和PDF文件</p>
+        </div>
+    </div>
+    <div class="page-actions">
+        <a href="{{ url_for('upload') }}" class="btn btn-primary">
+            <i class="bi bi-cloud-upload me-2"></i> 上传扫描件
+        </a>
+    </div>
 </div>
 
-<div class="card shadow-sm mb-4">
+<div class="card mb-4">
+    <div class="card-header">
+        <h3 class="card-header-title">搜索筛选</h3>
+    </div>
     <div class="card-body">
-        <form method="GET" action="{{ url_for('index') }}" class="row g-3 align-items-end">
-            <div class="col-md-2">
+        <form method="GET" action="{{ url_for('index') }}" class="search-form">
+            <div class="form-group">
                 <label class="form-label">版本名称</label>
                 <input type="text" name="version" class="form-control" value="{{ version }}" placeholder="模糊搜索">
             </div>
-            <div class="col-md-2">
+            <div class="form-group">
                 <label class="form-label">版本来源</label>
                 <input type="text" name="source" class="form-control" value="{{ source }}" placeholder="模糊搜索">
             </div>
-            <div class="col-md-2">
+            <div class="form-group">
                 <label class="form-label">提供人</label>
                 <input type="text" name="person" class="form-control" value="{{ person }}" placeholder="模糊搜索">
             </div>
-            <div class="col-md-2">
+            <div class="form-group">
                 <label class="form-label">文件类型</label>
                 <select name="file_type" class="form-select">
                     <option value="">全部</option>
@@ -115,19 +188,23 @@
                     <option value="PDF" {% if file_type == 'PDF' %}selected{% endif %}>PDF</option>
                 </select>
             </div>
-            <div class="col-md-4">
+            <div class="form-group" style="flex: 0.5;">
                 <button type="submit" class="btn btn-primary"><i class="bi bi-search"></i> 搜索</button>
-                <a href="{{ url_for('index') }}" class="btn btn-outline-secondary">重置</a>
+                <a href="{{ url_for('index') }}" class="btn btn-secondary">重置</a>
             </div>
         </form>
     </div>
 </div>
 
-<div class="card shadow-sm">
+<div class="card">
+    <div class="card-header">
+        <h3 class="card-header-title">扫描件列表</h3>
+        <span class="member-meta">共 {{ total }} 条记录</span>
+    </div>
     <div class="card-body p-0">
         <div class="table-responsive">
-            <table class="table table-hover mb-0">
-                <thead class="table-light">
+            <table class="table">
+                <thead>
                     <tr>
                         <th class="px-4">文件名</th>
                         <th>页码</th>
@@ -143,82 +220,89 @@
                         <td class="px-4">
                             <div class="d-flex align-items-center">
                                 <img src="{{ record.oss_url }}" class="thumbnail-img me-3 list-preview-img" data-url="{{ record.oss_url }}" data-page="{{ record.page_number or '' }}" title="点击预览" onclick="openImageViewer('{{ record.oss_url }}', '{{ record.page_number or '' }}')">
-                                <div class="text-break fw-bold">
-                                    {% if record.file_type == 'PDF' %}
-                                    <span class="badge bg-danger me-1">PDF</span>
-                                    {% elif record.file_type == '图片' %}
-                                    <span class="badge bg-success me-1">图片</span>
+                                <div class="text-break">
+                                    <div class="member-name">
+                                        {% if record.file_type == 'PDF' %}
+                                        <span class="file-type-badge pdf me-1">PDF</span>
+                                        {% elif record.file_type == '图片' %}
+                                        <span class="file-type-badge image me-1">图片</span>
+                                        {% endif %}
+                                        {{ record.file_name }}
+                                    </div>
+                                    {% if record.genealogy_version or record.genealogy_source %}
+                                    <div class="member-meta mt-1">
+                                        {% if record.genealogy_version %}
+                                        <span title="版本名称"><i class="bi bi-tag"></i> {{ record.genealogy_version }}</span>
+                                        {% endif %}
+                                        {% if record.genealogy_source %}
+                                        <span title="版本来源" class="ms-2"><i class="bi bi-archive"></i> {{ record.genealogy_source }}</span>
+                                        {% endif %}
+                                    </div>
                                     {% endif %}
-                                    {{ record.file_name }}
                                 </div>
                             </div>
                         </td>
                         <td>
                             {% if record.page_number %}
-                                <span class="badge bg-info text-dark">第 {{ record.page_number }} 页</span>
+                                <span class="badge badge-primary">第 {{ record.page_number }} 页</span>
                             {% else %}
-                                <span class="text-muted">未知</span>
+                                <span class="text-muted small">-</span>
                             {% endif %}
                         </td>
                         <td>
-                            <div class="fw-bold">{{ record.upload_person or '未知' }}</div>
-                            {% if record.genealogy_version or record.genealogy_source %}
-                            <div class="small text-muted mt-1" style="font-size: 0.75rem;">
-                                {% if record.genealogy_version %}
-                                <span title="版本名称"><i class="bi bi-tag"></i> {{ record.genealogy_version }}</span><br>
-                                {% endif %}
-                                {% if record.genealogy_source %}
-                                <span title="版本来源"><i class="bi bi-archive"></i> {{ record.genealogy_source }}</span>
-                                {% endif %}
-                            </div>
-                            {% endif %}
+                            <div style="color: var(--color-text-primary); font-weight: 500;">{{ record.upload_person or '未知' }}</div>
                         </td>
                         <td>
                             {% if record.ai_status == 2 %}
-                                <span class="badge bg-success"><i class="bi bi-check-circle"></i> 解析成功</span>
+                                <span class="badge badge-success"><i class="bi bi-check-circle"></i> 解析成功</span>
                             {% elif record.ai_status == 1 %}
-                                <span class="badge bg-warning text-dark"><i class="bi bi-hourglass-split"></i> 解析中...</span>
+                                <span class="badge badge-warning"><i class="bi bi-hourglass-split"></i> 解析中...</span>
                             {% elif record.ai_status == 3 %}
-                                <span class="badge bg-danger"><i class="bi bi-x-circle"></i> 解析失败</span>
+                                <span class="badge badge-danger"><i class="bi bi-x-circle"></i> 解析失败</span>
                             {% else %}
-                                <span class="badge bg-secondary">未解析</span>
+                                <span class="badge badge-secondary">未解析</span>
                             {% endif %}
                         </td>
-                        <td>{{ record.upload_time }}</td>
+                        <td><span class="small">{{ record.upload_time }}</span></td>
                         <td class="text-center">
-                            <button onclick="openImageViewer('{{ record.oss_url }}', '{{ record.page_number or '' }}')" class="btn btn-sm btn-outline-info list-preview-btn" data-url="{{ record.oss_url }}" data-page="{{ record.page_number or '' }}" title="查看原图">
-                                <i class="bi bi-eye"></i>
-                            </button>
-                            
-                            {% if record.ai_status == 2 %}
-                                <a href="{{ url_for('add_member', record_id=record.id) }}" class="btn btn-sm btn-success" title="查看解析并录入">
-                                    <i class="bi bi-magic"></i> 录入
-                                </a>
-                                <button onclick="reStartAiAnalysis({{ record.id }})" class="btn btn-sm btn-outline-warning" title="重新AI解析">
-                                    <i class="bi bi-arrow-repeat"></i> 重析
+                            <div class="table-action-btns">
+                                <button onclick="openImageViewer('{{ record.oss_url }}', '{{ record.page_number or '' }}')" class="btn btn-info" title="查看原图">
+                                    <i class="bi bi-eye"></i>
                                 </button>
-                            {% else %}
-                                <button onclick="startAiAnalysis({{ record.id }})" class="btn btn-sm btn-outline-primary" title="AI 解析">
-                                    <i class="bi bi-robot"></i> 解析
-                                </button>
-                                <a href="{{ url_for('add_member', record_id=record.id) }}" class="btn btn-sm btn-outline-secondary" title="手动录入">
-                                    <i class="bi bi-pencil"></i> 手动
-                                </a>
-                            {% endif %}
+                                
+                                {% if record.ai_status == 2 %}
+                                    <a href="{{ url_for('add_member', record_id=record.id) }}" class="btn btn-success" title="查看解析并录入">
+                                        <i class="bi bi-magic"></i> 录入
+                                    </a>
+                                    <button onclick="reStartAiAnalysis({{ record.id }})" class="btn btn-warning" title="重新AI解析">
+                                        <i class="bi bi-arrow-repeat"></i> 重析
+                                    </button>
+                                {% else %}
+                                    <button onclick="startAiAnalysis({{ record.id }})" class="btn btn-primary" title="AI 解析">
+                                        <i class="bi bi-robot"></i> 解析
+                                    </button>
+                                    <a href="{{ url_for('add_member', record_id=record.id) }}" class="btn btn-secondary" title="手动录入">
+                                        <i class="bi bi-pencil"></i> 手动
+                                    </a>
+                                {% endif %}
 
-                            <form action="{{ url_for('delete_upload', record_id=record.id) }}" method="POST" class="d-inline" onsubmit="return confirm('确定要删除此文件记录吗?此操作无法撤销。');">
-                                <button type="submit" class="btn btn-sm btn-outline-danger" title="删除">
-                                    <i class="bi bi-trash"></i>
-                                </button>
-                            </form>
+                                <form action="{{ url_for('delete_upload', record_id=record.id) }}" method="POST" class="d-inline" onsubmit="return confirm('确定要删除此文件记录吗?此操作无法撤销。');">
+                                    <button type="submit" class="btn btn-danger" title="删除">
+                                        <i class="bi bi-trash"></i>
+                                    </button>
+                                </form>
+                            </div>
                         </td>
                     </tr>
                     {% endfor %}
                     {% if not records %}
                     <tr>
-                        <td colspan="4" class="text-center py-5 text-muted">
-                            <i class="bi bi-folder-x fs-1 d-block mb-2"></i>
-                            暂无扫描件数据,请先上传。
+                        <td colspan="6" class="empty-state">
+                            <i class="bi bi-folder-x"></i>
+                            <div>暂无扫描件数据,请先上传。</div>
+                            <a href="{{ url_for('upload') }}" class="btn btn-primary mt-3">
+                                <i class="bi bi-cloud-upload"></i> 上传扫描件
+                            </a>
                         </td>
                     </tr>
                     {% endif %}
@@ -227,51 +311,52 @@
         </div>
     </div>
     {% if total_pages > 1 or total > 0 %}
-    <div class="card-footer d-flex justify-content-between align-items-center bg-white border-top-0 pt-3">
-        <div class="text-muted small">
-            共 <strong>{{ total }}</strong> 条记录,当前第 {{ page }} / {{ total_pages if total_pages > 0 else 1 }} 页
+    <div class="card-footer bg-white border-top-0 pt-4">
+        <div class="d-flex justify-content-between align-items-center">
+            <div class="text-muted small">
+                共 <strong>{{ total }}</strong> 条记录,当前第 {{ page }} / {{ total_pages if total_pages > 0 else 1 }} 页
+            </div>
+            {% if total_pages > 1 %}
+            <nav>
+                <ul class="pagination">
+                    <li class="page-item {% if page == 1 %}disabled{% endif %}">
+                        <a class="page-link" href="{{ url_for('index', page=page-1, version=version, source=source, person=person, file_type=file_type) }}">上一页</a>
+                    </li>
+                    
+                    {% set start_page = page - 2 if page - 2 > 0 else 1 %}
+                    {% set end_page = start_page + 4 if start_page + 4 <= total_pages else total_pages %}
+                    {% set start_page = end_page - 4 if end_page - 4 > 0 else 1 %}
+                    
+                    {% for p in range(start_page, end_page + 1) %}
+                    <li class="page-item {% if p == page %}active{% endif %}">
+                        <a class="page-link" href="{{ url_for('index', page=p, version=version, source=source, person=person, file_type=file_type) }}">{{ p }}</a>
+                    </li>
+                    {% endfor %}
+                    
+                    <li class="page-item {% if page == total_pages %}disabled{% endif %}">
+                        <a class="page-link" href="{{ url_for('index', page=page+1, version=version, source=source, person=person, file_type=file_type) }}">下一页</a>
+                    </li>
+                </ul>
+            </nav>
+            {% endif %}
         </div>
-        {% if total_pages > 1 %}
-        <nav>
-            <ul class="pagination pagination-sm mb-0">
-                <li class="page-item {% if page == 1 %}disabled{% endif %}">
-                    <a class="page-link" href="{{ url_for('index', page=page-1, version=version, source=source, person=person, file_type=file_type) }}">上一页</a>
-                </li>
-                
-                {% set start_page = page - 2 if page - 2 > 0 else 1 %}
-                {% set end_page = start_page + 4 if start_page + 4 <= total_pages else total_pages %}
-                {% set start_page = end_page - 4 if end_page - 4 > 0 else 1 %}
-                
-                {% for p in range(start_page, end_page + 1) %}
-                <li class="page-item {% if p == page %}active{% endif %}">
-                    <a class="page-link" href="{{ url_for('index', page=p, version=version, source=source, person=person, file_type=file_type) }}">{{ p }}</a>
-                </li>
-                {% endfor %}
-                
-                <li class="page-item {% if page == total_pages %}disabled{% endif %}">
-                    <a class="page-link" href="{{ url_for('index', page=page+1, version=version, source=source, person=person, file_type=file_type) }}">下一页</a>
-                </li>
-            </ul>
-        </nav>
-        {% endif %}
     </div>
     {% endif %}
 </div>
 
-<!-- Image Viewer Modal -->
 <div class="modal fade" id="imageModal" tabindex="-1" aria-hidden="true">
     <div class="modal-dialog modal-fullscreen">
-        <div class="modal-content bg-light">
-            <div class="modal-header py-2">
-                <h5 class="modal-title fs-6"><i class="bi bi-image"></i> 扫描件预览 <span id="modalPageNum" class="badge bg-secondary ms-2"></span></h5>
+        <div class="modal-content">
+            <div class="modal-header py-2" style="background: var(--color-bg-card); border-bottom: 1px solid var(--color-border);">
+                <h5 class="modal-title fs-6"><i class="bi bi-image"></i> 扫描件预览 <span id="modalPageNum" class="badge badge-secondary ms-2"></span></h5>
                 <div class="ms-auto me-3 text-muted small" id="listImageIndexIndicator"></div>
                 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
             </div>
             <div class="modal-body p-0 modal-image-container position-relative">
                 <div class="image-toolbar">
                     <div class="btn-group btn-group-sm">
-                        <button type="button" class="btn btn-outline-secondary" onclick="rotateImage(-90)" title="左旋90°"><i class="bi bi-arrow-counterclockwise"></i></button>
-                        <button type="button" class="btn btn-outline-secondary" onclick="rotateImage(90)" title="右旋90°"><i class="bi bi-arrow-clockwise"></i></button>
+                        <button type="button" class="btn btn-secondary" onclick="rotateImage(-90)" title="左旋90°"><i class="bi bi-arrow-counterclockwise"></i></button>
+                        <button type="button" class="btn btn-secondary" onclick="rotateImage(90)" title="右旋90°"><i class="bi bi-arrow-clockwise"></i></button>
                     </div>
                     <div class="d-flex align-items-center gap-2 mx-3 border-start border-end px-3">
                         <i class="bi bi-brightness-high" title="亮度"></i>
@@ -293,7 +378,7 @@
                     <i class="bi bi-chevron-right fs-4"></i>
                 </button>
 
-                <div class="image-viewer shadow-inner" id="viewer">
+                <div class="image-viewer" id="viewer">
                     <div id="magnifier" class="magnifier-glass"></div>
                     <div id="imageWrapper" class="image-wrapper">
                         <img id="refImage" src="" alt="家谱图片" draggable="false">
@@ -307,7 +392,6 @@
 
 {% block extra_js %}
 <script>
-    // --- Image Viewer Logic ---
     let imgRotation = 0;
     let imgBrightness = 100;
     let imgContrast = 100;
@@ -322,7 +406,6 @@
     let currentListIndex = 0;
 
     document.addEventListener('DOMContentLoaded', () => {
-        // Collect all previewable images from the current table
         const imgs = document.querySelectorAll('.list-preview-img');
         imgs.forEach((img, idx) => {
             currentListImages.push({
@@ -331,7 +414,6 @@
             });
         });
         
-        // Add click events to buttons
         document.getElementById('listPrevImageBtn').addEventListener('click', () => {
             if (currentListIndex > 0) openListImage(currentListIndex - 1);
         });
@@ -340,7 +422,6 @@
             if (currentListIndex < currentListImages.length - 1) openListImage(currentListIndex + 1);
         });
         
-        // Keyboard navigation
         document.getElementById('imageModal').addEventListener('keydown', (e) => {
             if (e.key === 'ArrowLeft' && currentListIndex > 0) {
                 openListImage(currentListIndex - 1);
@@ -358,7 +439,6 @@
     }
 
     function openImageViewer(url, pageNum) {
-        // Update index if opened via direct click instead of arrows
         const foundIdx = currentListImages.findIndex(img => img.url === url);
         if (foundIdx !== -1) currentListIndex = foundIdx;
         
@@ -371,7 +451,6 @@
         document.getElementById('refImage').src = url;
         document.getElementById('modalPageNum').textContent = pageNum ? '第 ' + pageNum + ' 页' : '';
         
-        // Update indicator and buttons
         if (currentListImages.length > 0 && foundIdx !== -1) {
             document.getElementById('listImageIndexIndicator').textContent = `${currentListIndex + 1} / ${currentListImages.length}`;
             document.getElementById('listPrevImageBtn').style.display = currentListIndex === 0 ? 'none' : 'block';
@@ -444,7 +523,6 @@
         const rect = refImage.getBoundingClientRect();
         const x = e.clientX - rect.left;
         const y = e.clientY - rect.top;
-        
         if (x < 0 || x > rect.width || y < 0 || y > rect.height) {
             magnifier.style.display = 'none';
             return;
@@ -454,7 +532,6 @@
         const viewerRect = viewer.getBoundingClientRect();
         magnifier.style.left = (e.clientX - viewerRect.left + glassOffset) + 'px';
         magnifier.style.top = (e.clientY - viewerRect.top + glassOffset) + 'px';
-        
         const zoom = 2.5;
         magnifier.style.backgroundImage = `url('${refImage.src}')`;
         magnifier.style.backgroundSize = `${rect.width * zoom}px ${rect.height * zoom}px`;
@@ -503,7 +580,6 @@
     function startAiAnalysis(recordId) {
         if (!confirm('确定要开始 AI 解析吗?这可能需要一些时间。')) return;
         
-        // Optimistic UI update
         const btn = event.currentTarget;
         const originalHtml = btn.innerHTML;
         btn.disabled = true;
@@ -518,7 +594,6 @@
         .then(response => response.json())
         .then(data => {
             if (data.success) {
-                // Reload to show status update (or update DOM dynamically)
                 window.location.reload();
             } else {
                 alert('启动解析失败: ' + data.message);
@@ -566,4 +641,4 @@
         });
     }
 </script>
-{% endblock %}
+{% endblock %}

+ 89 - 129
templates/layout.html

@@ -4,144 +4,106 @@
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
     <title>{% block title %}家谱管理系统{% endblock %}</title>
-    <!-- Local Bootstrap CSS -->
     <link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet">
-    <!-- Local Bootstrap Icons CSS -->
     <link href="{{ url_for('static', filename='css/bootstrap-icons.min.css') }}" rel="stylesheet">
-    <style>
-        body { font-family: 'Microsoft YaHei', sans-serif; }
-        .sidebar { min-height: 100vh; background-color: #343a40; color: white; }
-        .sidebar a { color: rgba(255,255,255,.8); text-decoration: none; padding: 10px 20px; display: block; }
-        .sidebar a:hover { background-color: #495057; color: white; }
-        .sidebar a.active { background-color: #0d6efd; color: white; }
-        .content-area { padding: 20px; }
-        
-        /* Watermark styles */
-        .watermark-container {
-            position: fixed;
-            top: 0;
-            left: 0;
-            width: 100%;
-            height: 100%;
-            pointer-events: none;
-            z-index: 9999;
-            overflow: hidden;
-            opacity: 0.08;
-        }
-        
-        .watermark-text {
-            position: absolute;
-            font-size: 18px;
-            color: #999;
-            transform: rotate(-15deg);
-            white-space: nowrap;
-            font-weight: 500;
-            letter-spacing: 2px;
-        }
-        
-        .watermark-corner {
-            position: fixed;
-            bottom: 15px;
-            right: 15px;
-            font-size: 12px;
-            color: #999;
-            opacity: 0.4;
-            text-align: right;
-            z-index: 1000;
-            pointer-events: none;
-        }
-    </style>
+    <link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet">
     {% block extra_css %}{% endblock %}
 </head>
-<body class="bg-light">
-    <div class="container-fluid">
-        <div class="row">
-            {% if session.get('user_id') %}
-            <!-- Sidebar -->
-            <div class="col-md-2 sidebar d-none d-md-block">
-                <div class="py-4 text-center border-bottom mb-4">
-                    <h4>家谱管理</h4>
+<body>
+    <div class="page-container">
+        {% if session.get('user_id') %}
+        <aside class="sidebar">
+            <div class="sidebar-header">
+                <picture>
+                    <source srcset="{{ url_for('static', filename='images/home/logo.webp') }}" type="image/webp">
+                    <img src="{{ url_for('static', filename='images/home/logo.png') }}" alt="留" class="sidebar-logo">
+                </picture>
+                <div class="sidebar-title">家谱管理系统</div>
+            </div>
+            
+            <nav class="sidebar-nav">
+                <a href="{{ url_for('home') }}" class="{% if request.endpoint == 'home' %}active{% endif %}">
+                    <i class="bi bi-house"></i> 系统首页
+                </a>
+                {% if session.get('is_super_admin') %}
+                <a href="{{ url_for('pdf_management') }}" class="{% if request.endpoint == 'pdf_management' %}active{% endif %}">
+                    <i class="bi bi-book"></i> 家谱管理
+                </a>
+                {% endif %}
+                <a href="{{ url_for('index') }}" class="{% if request.endpoint == 'index' %}active{% endif %}">
+                    <i class="bi bi-file-earmark-arrow-up"></i> 扫描件管理
+                </a>
+                <a href="{{ url_for('members') }}" class="{% if request.endpoint == 'members' %}active{% endif %}">
+                    <i class="bi bi-people"></i> 成员列表
+                </a>
+                <a href="{{ url_for('batch_genealogy') }}" class="{% if request.endpoint == 'batch_genealogy' %}active{% endif %}">
+                    <i class="bi bi-file-text"></i> 批量处理族谱原文
+                </a>
+                <a href="{{ url_for('tree') }}" class="{% if request.endpoint == 'tree' %}active{% endif %}">
+                    <i class="bi bi-diagram-3"></i> 家谱世系树状图
+                </a>
+                <a href="{{ url_for('tree_gen') }}" class="{% if request.endpoint == 'tree_gen' %}active{% endif %}">
+                    <i class="bi bi-layout-three-columns"></i> 世代分层树
+                </a>
+                <a href="{{ url_for('lineage_query') }}" class="{% if request.endpoint == 'lineage_query' %}active{% endif %}">
+                    <i class="bi bi-tree"></i> 世系查询
+                </a>
+                <a href="{{ url_for('settlements') }}" class="{% if request.endpoint == 'settlements' %}active{% endif %}">
+                    <i class="bi bi-globe"></i> 聚落地图
+                </a>
+                <a href="{{ url_for('generation_check_page') }}" class="{% if request.endpoint == 'generation_check_page' %}active{% endif %}">
+                    <i class="bi bi-clipboard2-check"></i> 代数核查
+                </a>
+                <a href="{{ url_for('suspected_errors') }}" class="{% if request.endpoint == 'suspected_errors' %}active{% endif %}">
+                    <i class="bi bi-exclamation-triangle"></i> 疑似错误汇总
+                </a>
+            </nav>
+            
+            <div class="sidebar-footer">
+                <div class="sidebar-user">
+                    <div class="sidebar-user-icon">{{ session['username'][0] if session['username'] else '用' }}</div>
+                    <span class="sidebar-user-name">{{ session['username'] }}</span>
                 </div>
-                <nav>
-                    <a href="{{ url_for('home') }}" class="{% if request.endpoint == 'home' %}active{% endif %}">
-                        <i class="bi bi-house me-2"></i> 系统首页
-                    </a>
-                    {% if session.get('is_super_admin') %}
-                    <a href="{{ url_for('pdf_management') }}" class="{% if request.endpoint == 'pdf_management' %}active{% endif %}">
-                        <i class="bi bi-book me-2"></i> 家谱管理
-                    </a>
-                    {% endif %}
-                    <a href="{{ url_for('index') }}" class="{% if request.endpoint == 'index' %}active{% endif %}">
-                        <i class="bi bi-file-earmark-arrow-up me-2"></i> 扫描件管理
-                    </a>
-                    <a href="{{ url_for('members') }}" class="{% if request.endpoint == 'members' %}active{% endif %}">
-                        <i class="bi bi-people me-2"></i> 成员列表
-                    </a>
-                    <a href="{{ url_for('batch_genealogy') }}" class="{% if request.endpoint == 'batch_genealogy' %}active{% endif %}">
-                        <i class="bi bi-file-text me-2"></i> 批量处理族谱原文
-                    </a>
-                    <a href="{{ url_for('tree') }}" class="{% if request.endpoint == 'tree' %}active{% endif %}">
-                        <i class="bi bi-diagram-3 me-2"></i> 家谱世系树状图
-                    </a>
-                    <a href="{{ url_for('tree_gen') }}" class="{% if request.endpoint == 'tree_gen' %}active{% endif %}">
-                        <i class="bi bi-layout-three-columns me-2"></i> 世代分层树
-                    </a>
-                    <a href="{{ url_for('lineage_query') }}" class="{% if request.endpoint == 'lineage_query' %}active{% endif %}">
-                        <i class="bi bi-tree me-2"></i> 世系查询
-                    </a>
-                    <a href="{{ url_for('settlements') }}" class="{% if request.endpoint == 'settlements' %}active{% endif %}">
-                        <i class="bi bi-globe me-2"></i> 聚落地图
-                    </a>
-                    <a href="{{ url_for('generation_check_page') }}" class="{% if request.endpoint == 'generation_check_page' %}active{% endif %}">
-                        <i class="bi bi-clipboard2-check me-2"></i> 代数核查
-                    </a>
-                    <div class="mt-5 border-top pt-3">
-                        <p class="px-3 small text-muted">用户: {{ session['username'] }}</p>
-                        <a href="{{ url_for('logout') }}" class="text-danger">
-                            <i class="bi bi-box-arrow-right me-2"></i> 退出登录
-                        </a>
-                    </div>
-                </nav>
+                <a href="{{ url_for('logout') }}" class="sidebar-logout">
+                    <i class="bi bi-box-arrow-right"></i> 退出登录
+                </a>
             </div>
-            {% endif %}
+        </aside>
+        {% endif %}
 
-            <!-- Watermark -->
-            <div class="watermark-container" id="watermarkContainer"></div>
-            <div class="watermark-corner" id="watermarkCorner"></div>
-            
-            <!-- Main Content -->
-            <div class="col-md-{% if session.get('user_id') %}10{% else %}12{% endif %} content-area">
-                {% with messages = get_flashed_messages(with_categories=true) %}
-                  {% if messages %}
-                    {% for category, message in messages %}
-                      {% if category == 'warning' %}
-                      <div class="alert alert-warning alert-dismissible fade show" role="alert">
-                        {{ message }}
-                        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
-                      </div>
-                      {% elif category == 'error' %}
-                      <div class="alert alert-danger alert-dismissible fade show" role="alert">
-                        {{ message }}
-                        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
-                      </div>
-                      {% else %}
-                      <div class="alert alert-info alert-dismissible fade show" role="alert">
-                        {{ message }}
-                        <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
-                      </div>
-                      {% endif %}
-                    {% endfor %}
+        <div class="watermark-container" id="watermarkContainer"></div>
+        <div class="watermark-corner" id="watermarkCorner"></div>
+
+        <main class="main-content">
+            {% with messages = get_flashed_messages(with_categories=true) %}
+              {% if messages %}
+                {% for category, message in messages %}
+                  {% if category == 'warning' %}
+                  <div class="alert alert-warning alert-dismissible fade show mb-4" role="alert">
+                    <i class="bi bi-exclamation-triangle me-2"></i>{{ message }}
+                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
+                  </div>
+                  {% elif category == 'error' %}
+                  <div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
+                    <i class="bi bi-x-circle me-2"></i>{{ message }}
+                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
+                  </div>
+                  {% else %}
+                  <div class="alert alert-info alert-dismissible fade show mb-4" role="alert">
+                    <i class="bi bi-info-circle me-2"></i>{{ message }}
+                    <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
+                  </div>
                   {% endif %}
-                {% endwith %}
+                {% endfor %}
+              {% endif %}
+            {% endwith %}
 
-                {% block content %}{% endblock %}
-            </div>
-        </div>
+            {% block content %}{% endblock %}
+        </main>
     </div>
-    <!-- Local Bootstrap JS -->
+
     <script src="{{ url_for('static', filename='js/bootstrap.bundle.min.js') }}"></script>
     
-    <!-- Watermark Script -->
     <script>
     document.addEventListener('DOMContentLoaded', function() {
         const username = "{{ session.get('username', '') }}";
@@ -159,7 +121,6 @@
             return `${username}_${year}${month}${day}_${hour}${minute}`;
         }
         
-        // Generate tiled watermark
         if (container && username) {
             const cols = 6;
             const rows = 8;
@@ -185,7 +146,6 @@
             setInterval(updateTiledWatermark, 60000);
         }
         
-        // Update corner watermark
         function updateCornerWatermark() {
             if (corner && username) {
                 corner.textContent = formatWatermarkText();
@@ -199,4 +159,4 @@
     
     {% block extra_js %}{% endblock %}
 </body>
-</html>
+</html>

+ 1 - 1
templates/lineage_query.html

@@ -11,7 +11,7 @@
         justify-content: center;
         height: calc(100vh - 155px);
         min-height: 400px;
-        color: rgba(255,255,255,0.5);
+        color: var(--color-text-muted);
     }
 
     /* ── 新竖列布局 ── */

+ 247 - 17
templates/login.html

@@ -2,33 +2,263 @@
 
 {% block title %}登录 - 家谱管理系统{% endblock %}
 
+{% block extra_css %}
+<style>
+    .login-page {
+        min-height: 100vh;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        position: relative;
+    }
+    
+    .login-bg {
+        position: fixed;
+        inset: 0;
+        z-index: 0;
+        background: url("{{ url_for('static', filename='images/home/bg.png') }}") center center / cover no-repeat;
+    }
+    @supports (background-image: image-set(url("{{ url_for('static', filename='images/home/bg.webp') }}") type("image/webp"))) {
+        .login-bg {
+            background-image: image-set(url("{{ url_for('static', filename='images/home/bg.webp') }}") type("image/webp"), url("{{ url_for('static', filename='images/home/bg.png') }}") type("image/png"));
+        }
+    }
+    
+    .login-container {
+        position: relative;
+        z-index: 10;
+        width: 100%;
+        max-width: 420px;
+        padding: var(--spacing-xl);
+    }
+    
+    .login-card {
+        background: rgba(255, 253, 249, 0.95);
+        backdrop-filter: blur(15px);
+        border-radius: 24px;
+        box-shadow: 0 12px 40px rgba(44, 24, 16, 0.12);
+        overflow: hidden;
+        border: 1px solid rgba(139, 105, 20, 0.1);
+    }
+    
+    .login-header {
+        padding: 40px 32px 28px;
+        text-align: center;
+        background: linear-gradient(180deg, rgba(139, 105, 20, 0.06) 0%, rgba(139, 105, 20, 0.02) 100%);
+        border-bottom: 1px solid rgba(139, 105, 20, 0.08);
+    }
+    
+    .login-logo {
+        width: 120px;
+        height: auto;
+        margin: 0 auto 16px;
+    }
+    
+    .login-title {
+        font-family: "HuakangWeibei", "STSong", serif;
+        font-size: 1.25rem;
+        font-weight: normal;
+        color: #1a1008;
+        letter-spacing: 0.08em;
+        text-align: center;
+        line-height: 1.4;
+        margin: 0;
+    }
+    
+    .login-subtitle {
+        font-family: "SourceHanSerif", "Noto Serif SC", serif;
+        font-size: 0.78rem;
+        color: #6b5840;
+        letter-spacing: 0.25em;
+        padding-left: 0.25em;
+        margin-top: 10px;
+    }
+    
+    .login-body {
+        padding: 32px 32px 28px;
+    }
+    
+    .form-group {
+        margin-bottom: 24px;
+    }
+    
+    .form-label {
+        font-family: "AdobeKaiti", "KaiTi", serif;
+        font-size: 0.85rem;
+        color: #5C4033;
+        margin-bottom: 8px;
+        display: block;
+        font-weight: 500;
+        letter-spacing: 0.08em;
+    }
+    
+    .input-wrapper {
+        position: relative;
+        background: white;
+        border-radius: 12px;
+        border: 1.5px solid rgba(139, 105, 20, 0.15);
+        transition: all 0.3s ease;
+        overflow: hidden;
+    }
+    
+    .input-wrapper:focus-within {
+        border-color: #8B6914;
+        box-shadow: 0 0 0 3px rgba(139, 105, 20, 0.08);
+    }
+    
+    .input-inner {
+        display: flex;
+        align-items: center;
+        padding: 0 14px;
+        height: 46px;
+    }
+    
+    .input-icon {
+        color: #8B7355;
+        font-size: 0.95rem;
+        margin-right: 12px;
+        width: 22px;
+        text-align: center;
+        flex-shrink: 0;
+    }
+    
+    .login-input {
+        flex: 1;
+        height: 100%;
+        border: none;
+        outline: none;
+        font-family: "SourceHanSerif", "Noto Serif SC", serif;
+        font-size: 0.95rem;
+        color: #2C1810;
+        background: transparent;
+    }
+    
+    .login-input::placeholder {
+        color: #8B7355;
+        opacity: 0.6;
+    }
+    
+    .login-btn {
+        width: 100%;
+        height: 48px;
+        font-size: 1rem;
+        font-family: "HuakangWeibei", "STSong", serif;
+        letter-spacing: 0.12em;
+        background: linear-gradient(135deg, #8B6914, #A68A37);
+        border: none;
+        border-radius: 12px;
+        color: white;
+        transition: all 0.3s ease;
+        position: relative;
+        overflow: hidden;
+        box-shadow: 0 4px 14px rgba(139, 105, 20, 0.25);
+        margin-top: 8px;
+    }
+    
+    .login-btn::before {
+        content: '';
+        position: absolute;
+        top: 0;
+        left: -100%;
+        width: 100%;
+        height: 100%;
+        background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.12), transparent);
+        transition: left 0.6s ease;
+    }
+    
+    .login-btn:hover::before {
+        left: 100%;
+    }
+    
+    .login-btn:hover {
+        background: linear-gradient(135deg, #7A5A10, #8B6914);
+        transform: translateY(-2px);
+        box-shadow: 0 8px 22px rgba(139, 105, 20, 0.35);
+    }
+    
+    .login-btn:active {
+        transform: translateY(0);
+        box-shadow: 0 4px 14px rgba(139, 105, 20, 0.25);
+    }
+    
+    .login-footer {
+        text-align: center;
+        margin-top: 24px;
+        padding-top: 16px;
+        border-top: 1px dashed rgba(139, 105, 20, 0.1);
+    }
+    
+    .login-footer-text {
+        font-size: 0.72rem;
+        color: #8B7355;
+        font-family: "SourceHanSerif", "Noto Serif SC", serif;
+        letter-spacing: 0.05em;
+    }
+    
+    @media (max-width: 480px) {
+        .login-container {
+            padding: var(--spacing-lg);
+        }
+        .login-card {
+            border-radius: 20px;
+        }
+        .login-header {
+            padding: 32px 24px 24px;
+        }
+        .login-logo {
+            width: 100px;
+        }
+        .login-title {
+            font-size: 1.1rem;
+        }
+        .login-body {
+            padding: 28px 24px 24px;
+        }
+    }
+</style>
+{% endblock %}
+
 {% block content %}
-<div class="row justify-content-center mt-5">
-    <div class="col-md-4">
-        <div class="card shadow">
-            <div class="card-header bg-primary text-white text-center py-3">
-                <h4 class="mb-0">用户登录</h4>
+<div class="login-page">
+    <div class="login-bg"></div>
+    
+    <div class="login-container">
+        <div class="login-card">
+            <div class="login-header">
+                <picture>
+                    <source srcset="{{ url_for('static', filename='images/home/logo.webp') }}" type="image/webp">
+                    <img src="{{ url_for('static', filename='images/home/logo.png') }}" alt="留" class="login-logo">
+                </picture>
+                <h1 class="login-title">留氏(衢州)族谱信息管理系统</h1>
+                <p class="login-subtitle">二〇二五年度版</p>
             </div>
-            <div class="card-body p-4">
+            <div class="login-body">
                 <form method="POST">
-                    <div class="mb-3">
+                    <div class="form-group">
                         <label class="form-label">用户名</label>
-                        <div class="input-group">
-                            <span class="input-group-text"><i class="bi bi-person"></i></span>
-                            <input type="text" name="username" class="form-control" placeholder="请输入用户名" required>
+                        <div class="input-wrapper">
+                            <div class="input-inner">
+                                <i class="bi bi-person input-icon"></i>
+                                <input type="text" name="username" class="login-input" placeholder="请输入用户名" required>
+                            </div>
                         </div>
                     </div>
-                    <div class="mb-4">
-                        <label class="form-label">密码</label>
-                        <div class="input-group">
-                            <span class="input-group-text"><i class="bi bi-lock"></i></span>
-                            <input type="password" name="password" class="form-control" placeholder="请输入密码" required>
+                    <div class="form-group">
+                        <label class="form-label">密 码</label>
+                        <div class="input-wrapper">
+                            <div class="input-inner">
+                                <i class="bi bi-lock input-icon"></i>
+                                <input type="password" name="password" class="login-input" placeholder="请输入密码" required>
+                            </div>
                         </div>
                     </div>
-                    <button type="submit" class="btn btn-primary w-100 py-2">立即登录</button>
+                    <button type="submit" class="login-btn">登 录</button>
                 </form>
+                <div class="login-footer">
+                    <p class="login-footer-text">© 2025 留氏(衢州)族谱信息管理系统</p>
+                </div>
             </div>
         </div>
     </div>
 </div>
-{% endblock %}
+{% endblock %}

+ 384 - 279
templates/member_detail.html

@@ -4,88 +4,138 @@
 
 {% block extra_css %}
 <style>
-    .info-label { color: #6c757d; font-weight: normal; width: 120px; display: inline-block; }
-    .info-value { color: #212529; font-weight: 500; }
-    .detail-section { background: white; border-radius: 8px; padding: 25px; margin-bottom: 20px; box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); }
-    .section-title { font-size: 1.1rem; font-weight: bold; border-bottom: 2px solid #f8f9fa; padding-bottom: 10px; margin-bottom: 20px; color: #0d6efd; }
-    .relation-card { border-left: 4px solid #198754; background: #f8fff9; padding: 10px 15px; margin-bottom: 10px; border-radius: 4px; }
+    .detail-section {
+        background: var(--color-bg-card);
+        border-radius: var(--radius-lg);
+        padding: var(--spacing-xl);
+        margin-bottom: var(--spacing-lg);
+        box-shadow: var(--shadow-card);
+    }
+    
+    .section-title {
+        font-family: var(--font-family-heading);
+        font-size: 1.1rem;
+        color: var(--color-secondary);
+        padding-left: var(--spacing-md);
+        border-left: 4px solid var(--color-primary);
+        margin-bottom: var(--spacing-lg);
+        letter-spacing: 0.05em;
+    }
+    
+    .info-row {
+        display: flex;
+        margin-bottom: var(--spacing-md);
+    }
+    
+    .info-label {
+        width: 120px;
+        font-family: var(--font-family-primary);
+        font-size: 0.85rem;
+        color: var(--color-text-muted);
+        flex-shrink: 0;
+    }
+    
+    .info-value {
+        font-family: var(--font-family-body);
+        font-size: 0.85rem;
+        color: var(--color-text-primary);
+        flex: 1;
+    }
+    
+    .relation-card {
+        border-left: 4px solid var(--color-primary);
+        background: rgba(139, 105, 20, 0.04);
+        padding: var(--spacing-md);
+        margin-bottom: var(--spacing-sm);
+        border-radius: 0 var(--radius-md) var(--radius-md) 0;
+    }
+    
+    .relation-card--child {
+        border-left-color: var(--color-info);
+        background: rgba(75, 122, 168, 0.04);
+    }
     
-    /* Image Preview in Sidebar */
     .source-image-preview {
         cursor: pointer;
         position: relative;
         overflow: hidden;
-        border: 1px solid #dee2e6;
-        border-radius: 4px;
-        transition: transform 0.2s;
+        border: 1px solid var(--color-border);
+        border-radius: var(--radius-md);
+        transition: all var(--transition-normal);
     }
+    
     .source-image-preview:hover {
         transform: scale(1.02);
-        box-shadow: 0 4px 8px rgba(0,0,0,0.1);
+        box-shadow: var(--shadow-md);
     }
+    
     .source-image-preview img {
         width: 100%;
         height: auto;
         display: block;
     }
+    
     .preview-overlay {
         position: absolute;
         bottom: 0;
         left: 0;
         right: 0;
-        background: rgba(0,0,0,0.6);
+        background: rgba(44, 24, 16, 0.6);
         color: white;
-        padding: 5px;
+        padding: var(--spacing-sm);
         text-align: center;
-        font-size: 0.8rem;
+        font-size: 0.75rem;
         opacity: 0;
-        transition: opacity 0.2s;
+        transition: opacity var(--transition-fast);
     }
+    
     .source-image-preview:hover .preview-overlay {
         opacity: 1;
     }
-    #detailImageTabNav .nav-link { cursor: pointer; font-size: 0.85rem; padding: 0.3rem 0.6rem; }
-    .image-empty-hint { color: #6c757d; font-size: 0.85rem; padding: 20px; text-align: center; }
+    
     .detail-preview-empty {
         min-height: 160px;
         display: flex;
         align-items: center;
         justify-content: center;
-        background: #f8f9fa;
-        border: 1px dashed #dee2e6;
-        border-radius: 4px;
-        color: #6c757d;
-        font-size: 0.9rem;
+        background: var(--color-bg-main);
+        border: 1px dashed var(--color-border);
+        border-radius: var(--radius-md);
+        color: var(--color-text-muted);
+        font-size: 0.85rem;
     }
-
-    /* Modal Image Viewer */
+    
     .modal-image-container {
         height: 80vh;
         display: flex;
         flex-direction: column;
     }
+    
     .image-toolbar {
-        background: #e9ecef;
-        padding: 5px 10px;
-        border-bottom: 1px solid #dee2e6;
+        background: var(--color-bg-card);
+        padding: var(--spacing-sm) var(--spacing-md);
+        border-bottom: 1px solid var(--color-border);
         display: flex;
-        gap: 10px;
+        gap: var(--spacing-md);
         align-items: center;
         flex-wrap: wrap;
     }
+    
     .image-viewer { 
         flex: 1; 
-        border: 1px solid #ccc; 
-        background: #f0f0f0; 
+        border: 1px solid var(--color-border); 
+        background: var(--color-bg-main); 
         overflow: hidden; 
         text-align: center; 
         position: relative; 
         cursor: grab;
         user-select: none;
     }
+    
     .image-viewer:active {
         cursor: grabbing;
     }
+    
     .image-wrapper {
         display: inline-block;
         transition: transform 0.2s ease-out;
@@ -95,125 +145,225 @@
         left: 50%;
         transform: translate(-50%, -50%);
     }
+    
     .image-wrapper img {
         max-width: 100%;
-        max-height: 100%; /* Fit within viewer initially */
+        max-height: 100%;
         display: block;
         pointer-events: none;
         user-select: none;
         transition: filter 0.2s;
-        box-shadow: 0 0 20px rgba(0,0,0,0.1);
+        box-shadow: var(--shadow-md);
     }
-    /* 放大镜样式 - Optional if we want it in modal too */
+    
     .magnifier-glass {
         position: absolute;
-        border: 3px solid #000;
+        border: 3px solid var(--color-primary);
         border-radius: 50%;
         cursor: none;
         width: 150px;
         height: 150px;
-        box-shadow: 0 0 10px rgba(0,0,0,0.5);
+        box-shadow: var(--shadow-lg);
         display: none;
         z-index: 1000;
         background-repeat: no-repeat;
         background-color: white;
         pointer-events: none;
     }
+    
+    .profile-header {
+        display: flex;
+        align-items: center;
+        gap: var(--spacing-lg);
+        margin-bottom: var(--spacing-xl);
+        padding-bottom: var(--spacing-xl);
+        border-bottom: 2px solid var(--color-border-light);
+    }
+    
+    .profile-avatar {
+        width: 80px;
+        height: 80px;
+        border-radius: 50%;
+        background: linear-gradient(135deg, var(--color-primary), var(--color-accent));
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        color: white;
+        font-family: var(--font-family-heading);
+        font-size: 1.8rem;
+        box-shadow: var(--shadow-md);
+    }
+    
+    .profile-info h1 {
+        font-family: var(--font-family-heading);
+        font-size: 1.6rem;
+        color: var(--color-secondary);
+        margin: 0;
+        letter-spacing: 0.05em;
+    }
+    
+    .profile-info p {
+        font-size: 0.85rem;
+        color: var(--color-text-muted);
+        margin-top: var(--spacing-xs);
+    }
+    
+    .adopt-alert {
+        background: rgba(201, 168, 61, 0.1);
+        border-left: 4px solid var(--color-warning);
+        padding: var(--spacing-sm) var(--spacing-md);
+        border-radius: 0 var(--radius-md) var(--radius-md) 0;
+        font-size: 0.85rem;
+        color: var(--color-warning);
+        margin-bottom: var(--spacing-md);
+    }
+    
+    .text-block {
+        background: var(--color-bg-main);
+        padding: var(--spacing-md);
+        border-radius: var(--radius-md);
+        font-size: 0.85rem;
+        color: var(--color-text-primary);
+    }
+    
+    .text-block--warning {
+        background: rgba(201, 168, 61, 0.1);
+        border: 1px solid rgba(201, 168, 61, 0.2);
+        color: var(--color-warning);
+    }
+    
+    .meta-info {
+        font-size: 0.75rem;
+        color: var(--color-text-muted);
+        text-align: center;
+        padding-top: var(--spacing-md);
+        border-top: 1px solid var(--color-border-light);
+    }
 </style>
 {% endblock %}
 
 {% block content %}
-<div class="container py-4">
-    <div class="d-flex justify-content-between align-items-center mb-4">
-        <h2><i class="bi bi-person-badge"></i> {{ member.name }} 的个人详情</h2>
-        <div class="btn-group">
-            <a href="{{ url_for('edit_member', member_id=member.id) }}" class="btn btn-primary">
-                <i class="bi bi-pencil"></i> 编辑信息
-            </a>
-            <a href="{{ url_for('members') }}" class="btn btn-outline-secondary">返回列表</a>
+<div class="page-header">
+    <div class="page-title">
+        <div class="page-title-icon">
+            <i class="bi bi-person-badge"></i>
+        </div>
+        <div>
+            <h1 class="page-title-text">成员详情</h1>
+            <p class="member-meta mt-1">查看成员详细信息</p>
         </div>
     </div>
+    <div class="page-actions">
+        <a href="{{ url_for('edit_member', member_id=member.id) }}" class="btn btn-primary">
+            <i class="bi bi-pencil"></i> 编辑信息
+        </a>
+        <a href="{{ url_for('members') }}" class="btn btn-secondary">返回列表</a>
+    </div>
+</div>
 
-    <div class="row">
-        <!-- 基本与核心信息 -->
-        <div class="col-md-8">
-            <div class="detail-section">
-                <div class="section-title">基本信息</div>
-                {% if adopt_info %}
-                <div class="alert alert-warning d-flex align-items-center mb-3 py-2 px-3" role="alert" style="border-left: 4px solid #fd7e14; background:#fff8f0;">
-                    <i class="bi bi-house-door-fill me-2 text-warning"></i>
-                    <span class="fw-bold">{{ adopt_info }}</span>
-                </div>
-                {% endif %}
-                <div class="row g-3">
-                    <div class="col-md-6">
+<div class="profile-header">
+    <div class="profile-avatar">{{ member.name[0] }}</div>
+    <div class="profile-info">
+        <h1>{{ member.name }}{% if member.simplified_name %}({{ member.simplified_name }}){% endif %}</h1>
+        <p>{{ member.name_word_generation or '' }} · {{ '男' if member.sex == 1 else '女' }} · {{ member.birthday_str or '未知出生日期' }}</p>
+    </div>
+</div>
+
+<div class="row">
+    <div class="col-md-8">
+        <div class="detail-section">
+            {% if adopt_info %}
+            <div class="adopt-alert">
+                <i class="bi bi-house-door-fill me-2"></i>
+                <span>{{ adopt_info }}</span>
+            </div>
+            {% endif %}
+            
+            <div class="section-title">基本信息</div>
+            <div class="row g-4">
+                <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">姓名(繁体):</span>
                         <span class="info-value">{{ member.name }}</span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">姓名(简体):</span>
                         <span class="info-value">{{ member.simplified_name or '-' }}</span>
                     </div>
-                    <div class="col-md-12">
-                        <span class="info-label d-block mb-1">族谱原文(繁体):</span>
-                        <span class="info-value">{{ member.genealogy_original_traditional if member.genealogy_original_traditional and member.genealogy_original_traditional != 'None' else '-' }}</span>
-                    </div>
-                    <div class="col-md-12">
-                        <span class="info-label d-block mb-1">族谱原文(简体):</span>
-                        <span class="info-value">{{ member.genealogy_original_simplified if member.genealogy_original_simplified and member.genealogy_original_simplified != 'None' else '-' }}</span>
-                    </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">性别:</span>
                         <span class="info-value">{{ '男' if member.sex == 1 else '女' }}</span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">曾用名:</span>
                         <span class="info-value">{{ member.former_name or '-' }}</span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">幼名/乳名:</span>
                         <span class="info-value">{{ member.childhood_name or '-' }}</span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">出生日期:</span>
                         <span class="info-value">{{ member.birthday_str or '未知' }}</span>
                     </div>
-                    <div class="col-md-6">
+                </div>
+                <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">民族:</span>
                         <span class="info-value">{{ member.nation or '-' }}</span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">状态:</span>
                         <span class="info-value">
                             {% if member.is_pass_away == 1 %}
-                                <span class="text-danger">已故</span>
+                                <span class="badge badge-secondary">已故</span>
                             {% elif member.is_pass_away == 2 %}
-                                <span class="text-warning">未知</span>
+                                <span class="badge badge-warning">未知</span>
                             {% else %}
-                                <span class="text-success">健在</span>
+                                <span class="badge badge-success">健在</span>
                             {% endif %}
                         </span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">婚姻状况:</span>
                         <span class="info-value">
                             {% set marital_map = {0: '未知', 1: '未婚', 2: '已婚', 3: '离异/丧偶'} %}
                             {{ marital_map.get(member.marital_status, '未知') }}
                         </span>
                     </div>
+                    <div class="info-row">
+                        <span class="info-label d-block mb-1">族谱原文(繁体):</span>
+                        <span class="info-value">{{ member.genealogy_original_traditional if member.genealogy_original_traditional and member.genealogy_original_traditional != 'None' else '-' }}</span>
+                    </div>
+                    <div class="info-row">
+                        <span class="info-label d-block mb-1">族谱原文(简体):</span>
+                        <span class="info-value">{{ member.genealogy_original_simplified if member.genealogy_original_simplified and member.genealogy_original_simplified != 'None' else '-' }}</span>
+                    </div>
                 </div>
+            </div>
 
-                <div class="section-title mt-4">谱系资料</div>
-                <div class="row g-3">
-                    <div class="col-md-6">
+            <div class="section-title">谱系资料</div>
+            <div class="row g-4">
+                <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">字辈:</span>
                         <span class="info-value">{{ member.name_word or '-' }}</span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">堂内排行:</span>
                         <span class="info-value">{{ member.family_rank or '-' }}</span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
+                        <span class="info-label">名号/封号:</span>
+                        <span class="info-value">{{ member.name_title or '-' }}</span>
+                    </div>
+                    <div class="info-row">
+                        <span class="info-label">分房/堂号:</span>
+                        <span class="info-value">{{ member.branch_family_hall or '-' }}</span>
+                    </div>
+                </div>
+                <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">世系世代:</span>
                         <span class="info-value">
                             {% if member.name_word_generation %}
@@ -221,8 +371,8 @@
                                 <span class="d-inline-flex flex-wrap gap-1 align-items-center">
                                 {% for gen in generations %}
                                     {% if gen.strip() %}
-                                    <span class="badge rounded-pill px-3 py-1" style="background: linear-gradient(135deg, #e8f0fe, #d2e3fc); color: #1a56db; font-weight: 500; font-size: 0.8rem; letter-spacing: 0.5px; border: 1px solid rgba(26,86,219,0.15);">
-                                        <i class="bi bi-diagram-3 me-1" style="font-size: 0.7rem;"></i>{{ gen.strip() }}
+                                    <span class="lineage-badge">
+                                        <i class="bi bi-diagram-3"></i>{{ gen.strip() }}
                                     </span>
                                     {% endif %}
                                 {% endfor %}
@@ -232,194 +382,185 @@
                             {% endif %}
                         </span>
                     </div>
-                    <div class="col-md-6">
-                        <span class="info-label">名号/封号:</span>
-                        <span class="info-value">{{ member.name_title or '-' }}</span>
-                    </div>
-                    <div class="col-md-6">
-                        <span class="info-label">分房/堂号:</span>
-                        <span class="info-value">{{ member.branch_family_hall or '-' }}</span>
-                    </div>
-                    <div class="col-md-12">
+                    <div class="info-row">
                         <span class="info-label">聚居地:</span>
                         <span class="info-value">{{ member.cluster_place or '-' }}</span>
                     </div>
                 </div>
+            </div>
 
-                <div class="section-title mt-4">联系与地址</div>
-                <div class="row g-3">
-                    <div class="col-md-6">
+            <div class="section-title">联系与地址</div>
+            <div class="row g-4">
+                <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">手机号:</span>
                         <span class="info-value">{{ member.phone or '-' }}</span>
                     </div>
-                    <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">微信号:</span>
                         <span class="info-value">{{ member.wechat_account or '-' }}</span>
                     </div>
-                    <div class="col-md-12">
+                </div>
+                <div class="col-md-6">
+                    <div class="info-row">
                         <span class="info-label">现居住址:</span>
                         <span class="info-value">{{ member.residential_address or '-' }}</span>
                     </div>
                 </div>
             </div>
+        </div>
 
-            <div class="detail-section">
-                <div class="section-title">个人履历与成就</div>
-                <div class="mb-4">
-                    <label class="info-label d-block mb-2">标签:</label>
-                    <div class="p-3 bg-light rounded">{{ member.tags or '暂无' }}</div>
-                </div>
-                <div class="mb-4">
-                    <label class="info-label d-block mb-2">人员备注:</label>
-                    <div class="p-3 bg-light rounded">{{ member.notes or '暂无' }}</div>
-                </div>
-                <div class="mb-4">
-                    <label class="info-label d-block mb-2">疑似错误标注:</label>
-                    <div class="p-3 bg-warning bg-opacity-10 border border-warning rounded text-warning">
-                        {{ member.suspected_error or '暂无' }}
-                    </div>
-                </div>
-                <div class="mb-4">
-                    <label class="info-label d-block mb-2">职业背景:</label>
-                    <div class="p-3 bg-light rounded">{{ member.occupation or '暂无信息' }}</div>
-                </div>
-                <div class="mb-4">
-                    <label class="info-label d-block mb-2">教育经历:</label>
-                    <div class="p-3 bg-light rounded">{{ member.educational or '暂无信息' }}</div>
+        <div class="detail-section">
+            <div class="section-title">个人履历与成就</div>
+            <div class="mb-4">
+                <label class="info-label d-block mb-2">标签:</label>
+                <div class="text-block">{{ member.tags or '暂无' }}</div>
+            </div>
+            <div class="mb-4">
+                <label class="info-label d-block mb-2">人员备注:</label>
+                <div class="text-block">{{ member.notes or '暂无' }}</div>
+            </div>
+            <div class="mb-4">
+                <label class="info-label d-block mb-2">疑似错误标注:</label>
+                <div class="text-block text-block--warning">{{ member.suspected_error or '暂无' }}</div>
+            </div>
+            <div class="mb-4">
+                <label class="info-label d-block mb-2">职业背景:</label>
+                <div class="text-block">{{ member.occupation or '暂无信息' }}</div>
+            </div>
+            <div class="mb-4">
+                <label class="info-label d-block mb-2">教育经历:</label>
+                <div class="text-block">{{ member.educational or '暂无信息' }}</div>
+            </div>
+            <div>
+                <label class="info-label d-block mb-2">个人成就:</label>
+                <div class="text-block">{{ member.personal_achievements or '暂无信息' }}</div>
+            </div>
+        </div>
+    </div>
+
+    <div class="col-md-4">
+        {% if member.source_image_url or member.reference_image_url %}
+        <div class="detail-section">
+            <div class="section-title">家谱图片</div>
+            <ul class="nav nav-tabs mb-3">
+                <li class="nav-item">
+                    <button type="button" class="nav-link" id="detail-tab-scan" onclick="switchDetailImageTab('scan')">
+                        <i class="bi bi-file-earmark-image"></i> 扫描件
+                    </button>
+                </li>
+                <li class="nav-item">
+                    <button type="button" class="nav-link" id="detail-tab-reference" onclick="switchDetailImageTab('reference')">
+                        <i class="bi bi-file-earmark-plus"></i> 参考件
+                    </button>
+                </li>
+            </ul>
+            <div class="source-image-preview" onclick="openImageViewer()">
+                <img id="detailPreviewImage" src="" alt="家谱图片" style="display: none;">
+                <div id="detailPreviewEmpty" class="detail-preview-empty" style="display: none;">
+                    <span><i class="bi bi-image"></i> 暂无图片</span>
                 </div>
-                <div>
-                    <label class="info-label d-block mb-2">个人成就:</label>
-                    <div class="p-3 bg-light rounded">{{ member.personal_achievements or '暂无信息' }}</div>
+                <div class="preview-overlay">
+                    <i class="bi bi-arrows-fullscreen"></i> <span id="detailPreviewHint">点击查看大图</span>
                 </div>
             </div>
+            <div class="mt-3 small text-muted bg-light p-2 rounded" id="detailScanMeta">
+                <div class="mb-1"><i class="bi bi-journal-text me-1"></i><strong>版本名称:</strong> {{ member.genealogy_version or '未提供' }}</div>
+                <div class="mb-1"><i class="bi bi-archive me-1"></i><strong>版本来源:</strong> {{ member.genealogy_source or '未提供' }}</div>
+                <div><i class="bi bi-person me-1"></i><strong>文件提供人:</strong> {{ member.upload_person or '未提供' }}</div>
+            </div>
+            <div class="mt-3 small text-muted bg-light p-2 rounded" id="detailReferenceMeta" style="display: none;">
+                <div class="mb-1"><i class="bi bi-file-earmark me-1"></i><strong>文件名:</strong> {{ member.reference_file_name or '未提供' }}</div>
+                <div><i class="bi bi-clock me-1"></i><strong>上传时间:</strong> {{ member.reference_upload_time or '未提供' }}</div>
+            </div>
         </div>
+        {% endif %}
 
-        <!-- 关系信息与原图 -->
-        <div class="col-md-4">
+        <div class="detail-section">
+            <div class="section-title">家族关系</div>
             
-            {% if member.source_image_url or member.reference_image_url %}
-            <div class="detail-section">
-                <div class="section-title">家谱图片</div>
-                <ul class="nav nav-tabs mb-3" id="detailImageTabNav">
-                    <li class="nav-item">
-                        <button type="button" class="nav-link" id="detail-tab-scan" onclick="switchDetailImageTab('scan')">
-                            <i class="bi bi-file-earmark-image"></i> 查看扫描件
-                        </button>
-                    </li>
-                    <li class="nav-item">
-                        <button type="button" class="nav-link" id="detail-tab-reference" onclick="switchDetailImageTab('reference')">
-                            <i class="bi bi-file-earmark-plus"></i> 查看参考件
-                        </button>
-                    </li>
-                </ul>
-                <div class="source-image-preview" onclick="openImageViewer()">
-                    <img id="detailPreviewImage" src="" alt="家谱图片" style="display: none;">
-                    <div id="detailPreviewEmpty" class="detail-preview-empty" style="display: none;">
-                        <span><i class="bi bi-image"></i> 暂无扫描件</span>
-                    </div>
-                    <div class="preview-overlay">
-                        <i class="bi bi-arrows-fullscreen"></i> <span id="detailPreviewHint">点击查看大图</span>
+            <h6 class="fw-bold mb-3" style="color: var(--color-text-secondary);"><i class="bi bi-arrow-up-circle me-1"></i> 尊辈/关联人</h6>
+            {% set child_order_labels = {1: '长子', 2: '次子', 3: '三子', 4: '四子', 5: '五子', 6: '六子', 7: '七子', 8: '八子', 9: '九子', 10: '十子'} %}
+            {% for p in parents %}
+            <div class="relation-card">
+                <div class="d-flex justify-content-between align-items-center">
+                    <a href="{{ url_for('member_detail', member_id=p.id) }}" class="text-decoration-none fw-bold" style="color: var(--color-primary);">
+                        {{ p.name }}
+                    </a>
+                    <div class="d-flex gap-2">
+                        <span class="badge badge-success">
+                            {% set rel_map = {1: '父亲', 2: '母亲', 10: '配偶', 11: '兄弟', 12: '姐妹'} %}
+                            {{ rel_map.get(p.relation_type, '关联人') }}
+                        </span>
+                        {% if p.relation_type == 1 and p.child_order %}
+                        <span class="badge badge-info">
+                            {{ child_order_labels.get(p.child_order, '第' ~ p.child_order ~ '子') }}
+                        </span>
+                        {% endif %}
+                        {% if p.sub_relation_type %}
+                        <span class="badge badge-warning">
+                            {% set sub_rel_map = {0: '亲生', 1: '养父', 2: '出继', 3: '入继', 10: '妾', 11: '外室'} %}
+                            {{ sub_rel_map.get(p.sub_relation_type, '') }}
+                        </span>
+                        {% endif %}
                     </div>
                 </div>
-                <div class="mt-3 small text-muted bg-light p-2 rounded" id="detailScanMeta">
-                    <div class="mb-1"><i class="bi bi-journal-text me-1"></i><strong>版本名称:</strong> {{ member.genealogy_version or '未提供' }}</div>
-                    <div class="mb-1"><i class="bi bi-archive me-1"></i><strong>版本来源:</strong> {{ member.genealogy_source or '未提供' }}</div>
-                    <div><i class="bi bi-person me-1"></i><strong>文件提供人:</strong> {{ member.upload_person or '未提供' }}</div>
-                </div>
-                <div class="mt-3 small text-muted bg-light p-2 rounded" id="detailReferenceMeta" style="display: none;">
-                    <div class="mb-1"><i class="bi bi-file-earmark me-1"></i><strong>文件名:</strong> {{ member.reference_file_name or '未提供' }}</div>
-                    <div><i class="bi bi-clock me-1"></i><strong>上传时间:</strong> {{ member.reference_upload_time or '未提供' }}</div>
-                </div>
             </div>
+            {% endfor %}
+            {% if not parents %}
+            <p class="text-muted small">暂无上层关系记录</p>
             {% endif %}
 
-            <div class="detail-section">
-                <div class="section-title">家族关系</div>
-                
-                <h6 class="fw-bold mb-3"><i class="bi bi-arrow-up-circle me-1"></i> 尊辈/关联人</h6>
-                {% set child_order_labels = {1: '长子', 2: '次子', 3: '三子', 4: '四子', 5: '五子', 6: '六子', 7: '七子', 8: '八子', 9: '九子', 10: '十子'} %}
-                {% for p in parents %}
-                <div class="relation-card">
-                    <div class="d-flex justify-content-between align-items-center">
-                        <a href="{{ url_for('member_detail', member_id=p.id) }}" class="text-decoration-none fw-bold">
-                            {{ p.name }}
-                        </a>
-                        <div class="d-flex gap-2">
-                            <span class="badge bg-success small">
-                                {% set rel_map = {1: '父亲', 2: '母亲', 10: '配偶', 11: '兄弟', 12: '姐妹'} %}
-                                {{ rel_map.get(p.relation_type, '关联人') }}
-                            </span>
-                            {% if p.relation_type == 1 and p.child_order %}
-                            <span class="badge bg-info small">
-                                {{ child_order_labels.get(p.child_order, '第' ~ p.child_order ~ '子') }}
-                            </span>
-                            {% endif %}
-                            {% if p.sub_relation_type %}
-                            <span class="badge bg-warning small">
-                                {% set sub_rel_map = {0: '亲生', 1: '养父', 2: '出继', 3: '入继', 10: '妾', 11: '外室'} %}
-                                {{ sub_rel_map.get(p.sub_relation_type, '') }}
-                            </span>
-                            {% endif %}
-                        </div>
-                    </div>
-                </div>
-                {% endfor %}
-                {% if not parents %}
-                <p class="text-muted small">暂无上层关系记录</p>
-                {% endif %}
-
-                <h6 class="fw-bold mt-4 mb-3"><i class="bi bi-arrow-down-circle me-1"></i> 子嗣/晚辈</h6>
-                {% for c in children %}
-                <div class="relation-card" style="border-left-color: #0d6efd; background: #f0f7ff;">
-                    <div class="d-flex justify-content-between align-items-center">
-                        <a href="{{ url_for('member_detail', member_id=c.id) }}" class="text-decoration-none fw-bold">
-                            {{ c.name }}
-                        </a>
-                        <div class="d-flex gap-2">
-                            <span class="badge bg-primary small">
-                                {% set rel_map = {1: '子女', 2: '子女', 10: '配偶', 11: '兄弟', 12: '姐妹'} %}
-                                {{ rel_map.get(c.relation_type, '后辈') }}
-                            </span>
-                            {% if c.child_order %}
-                            <span class="badge bg-info small">
-                                {{ child_order_labels.get(c.child_order, '第' ~ c.child_order ~ '子') }}
-                            </span>
-                            {% endif %}
-                            {% if c.sub_relation_type %}
-                            <span class="badge bg-warning small">
-                                {% set sub_rel_map = {0: '亲生', 1: '养父', 2: '出继', 3: '入继', 10: '妾', 11: '外室'} %}
-                                {{ sub_rel_map.get(c.sub_relation_type, '') }}
-                            </span>
-                            {% endif %}
-                        </div>
+            <h6 class="fw-bold mt-4 mb-3" style="color: var(--color-text-secondary);"><i class="bi bi-arrow-down-circle me-1"></i> 子嗣/晚辈</h6>
+            {% for c in children %}
+            <div class="relation-card relation-card--child">
+                <div class="d-flex justify-content-between align-items-center">
+                    <a href="{{ url_for('member_detail', member_id=c.id) }}" class="text-decoration-none fw-bold" style="color: var(--color-info);">
+                        {{ c.name }}
+                    </a>
+                    <div class="d-flex gap-2">
+                        <span class="badge badge-info">
+                            {% set rel_map = {1: '子女', 2: '子女', 10: '配偶', 11: '兄弟', 12: '姐妹'} %}
+                            {{ rel_map.get(c.relation_type, '后辈') }}
+                        </span>
+                        {% if c.child_order %}
+                        <span class="badge badge-primary">
+                            {{ child_order_labels.get(c.child_order, '第' ~ c.child_order ~ '子') }}
+                        </span>
+                        {% endif %}
+                        {% if c.sub_relation_type %}
+                        <span class="badge badge-warning">
+                            {% set sub_rel_map = {0: '亲生', 1: '养父', 2: '出继', 3: '入继', 10: '妾', 11: '外室'} %}
+                            {{ sub_rel_map.get(c.sub_relation_type, '') }}
+                        </span>
+                        {% endif %}
                     </div>
                 </div>
-                {% endfor %}
-                {% if not children %}
-                <p class="text-muted small">暂无下层关系记录</p>
-                {% endif %}
             </div>
+            {% endfor %}
+            {% if not children %}
+            <p class="text-muted small">暂无下层关系记录</p>
+            {% endif %}
+        </div>
 
-            <div class="detail-section text-center">
-                <div class="section-title">系统操作</div>
-                <div class="small text-muted mb-3">
-                    记录创建于:{{ member.create_time }}<br>
-                    最后修改:{{ member.modified_time }}
-                </div>
-                <button class="btn btn-sm btn-outline-danger w-100" onclick="confirmDelete()">
-                    <i class="bi bi-trash"></i> 删除此成员
-                </button>
-                <form id="deleteForm" action="{{ url_for('delete_member', member_id=member.id) }}" method="POST" style="display: none;"></form>
+        <div class="detail-section">
+            <div class="section-title">系统操作</div>
+            <div class="meta-info">
+                记录创建于:{{ member.create_time }}<br>
+                最后修改:{{ member.modified_time }}
             </div>
+            <button class="btn btn-danger w-100 mt-4" onclick="confirmDelete()">
+                <i class="bi bi-trash"></i> 删除此成员
+            </button>
+            <form id="deleteForm" action="{{ url_for('delete_member', member_id=member.id) }}" method="POST" style="display: none;"></form>
         </div>
     </div>
 </div>
 
-<!-- Image Viewer Modal -->
 <div class="modal fade" id="imageModal" tabindex="-1" aria-hidden="true">
     <div class="modal-dialog modal-fullscreen">
-        <div class="modal-content bg-light">
-            <div class="modal-header py-2">
+        <div class="modal-content">
+            <div class="modal-header py-2" style="background: var(--color-bg-card); border-bottom: 1px solid var(--color-border);">
                 <h5 class="modal-title fs-6"><i class="bi bi-image"></i> <span id="modalImageTitle">家谱图片查看</span></h5>
                 <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
             </div>
@@ -427,19 +568,19 @@
                 <ul class="nav nav-tabs px-3 pt-2 bg-white border-bottom" id="modalImageTabNav">
                     <li class="nav-item">
                         <button type="button" class="nav-link" id="modal-tab-scan" onclick="switchModalImageTab('scan')">
-                            <i class="bi bi-file-earmark-image"></i> 查看扫描件
+                            <i class="bi bi-file-earmark-image"></i> 扫描件
                         </button>
                     </li>
                     <li class="nav-item">
                         <button type="button" class="nav-link" id="modal-tab-reference" onclick="switchModalImageTab('reference')">
-                            <i class="bi bi-file-earmark-plus"></i> 查看参考件
+                            <i class="bi bi-file-earmark-plus"></i> 参考件
                         </button>
                     </li>
                 </ul>
                 <div class="image-toolbar">
                     <div class="btn-group btn-group-sm">
-                        <button type="button" class="btn btn-outline-secondary" onclick="rotateImage(-90)" title="左旋90°"><i class="bi bi-arrow-counterclockwise"></i></button>
-                        <button type="button" class="btn btn-outline-secondary" onclick="rotateImage(90)" title="右旋90°"><i class="bi bi-arrow-clockwise"></i></button>
+                        <button type="button" class="btn btn-secondary" onclick="rotateImage(-90)" title="左旋90°"><i class="bi bi-arrow-counterclockwise"></i></button>
+                        <button type="button" class="btn btn-secondary" onclick="rotateImage(90)" title="右旋90°"><i class="bi bi-arrow-clockwise"></i></button>
                     </div>
                     <div class="d-flex align-items-center gap-2 mx-3 border-start border-end px-3">
                         <i class="bi bi-brightness-high" title="亮度"></i>
@@ -453,11 +594,11 @@
                         <label class="form-check-label small" for="magnifierSwitch">🔍 放大镜</label>
                     </div>
                 </div>
-                <div class="image-viewer shadow-inner" id="viewer">
+                <div class="image-viewer" id="viewer">
                     <div id="magnifier" class="magnifier-glass"></div>
                     <div id="imageWrapper" class="image-wrapper">
                         <img id="refImage" src="" alt="家谱图片" draggable="false" style="display: none;">
-                        <div id="modalEmptyState" class="image-empty-hint" style="display: none;">暂无图片</div>
+                        <div id="modalEmptyState" class="detail-preview-empty" style="display: none;">暂无图片</div>
                     </div>
                 </div>
             </div>
@@ -531,7 +672,7 @@
         } else {
             img.style.display = 'none';
             img.removeAttribute('src');
-            emptyState.style.display = 'block';
+            emptyState.style.display = 'flex';
             emptyState.textContent = modalActiveTab === 'scan' ? '暂无扫描件' : '暂无参考件';
             title.textContent = '家谱图片查看';
         }
@@ -542,7 +683,7 @@
     }
 
     document.addEventListener('DOMContentLoaded', function() {
-        if (document.getElementById('detailImageTabNav')) {
+        if (document.getElementById('detail-tab-scan')) {
             switchDetailImageTab(detailActiveTab);
         }
     });
@@ -560,33 +701,23 @@
         myModal.show();
     }
 
-    // --- Image Viewer Logic (Reused from add_member) ---
-    // Image State
     let imgRotation = 0;
     let imgBrightness = 100;
     let imgContrast = 100;
-    
-    // Dragging State
     let isDragging = false;
     let hasDragged = false;
     let startX = 0, startY = 0;
-    let currentX = 0, currentY = 0; // Relative to center
-
-    // Zoom State
+    let currentX = 0, currentY = 0;
     let isZoomedIn = false;
     const ZOOM_LEVEL = 2.0;
 
-    // Elements
     const viewer = document.getElementById('viewer');
     const magnifier = document.getElementById('magnifier');
     const magnifierSwitch = document.getElementById('magnifierSwitch');
     const imageWrapper = document.getElementById('imageWrapper');
     const refImage = document.getElementById('refImage');
     
-    // Initialize Dragging and Zooming
     if (imageWrapper) {
-        // Center initial position logic is handled by CSS (top 50% left 50% translate -50%)
-        
         viewer.style.cursor = 'zoom-in';
 
         viewer.addEventListener('mousedown', (e) => {
@@ -595,27 +726,19 @@
             hasDragged = false;
             startX = e.clientX;
             startY = e.clientY;
-            
             viewer.style.cursor = 'grabbing';
             e.preventDefault(); 
         });
         
         window.addEventListener('mousemove', (e) => {
             if (!isDragging) return;
-            
             const dx = e.clientX - startX;
             const dy = e.clientY - startY;
-            
-            if (Math.abs(dx) > 2 || Math.abs(dy) > 2) {
-                hasDragged = true;
-            }
-
+            if (Math.abs(dx) > 2 || Math.abs(dy) > 2) hasDragged = true;
             currentX += dx;
             currentY += dy;
-            
             startX = e.clientX;
             startY = e.clientY;
-            
             updateImageTransform();
         });
         
@@ -623,10 +746,7 @@
             if (isDragging) {
                 isDragging = false;
                 viewer.style.cursor = isZoomedIn ? 'grab' : 'zoom-in';
-                
-                if (!hasDragged && viewer.contains(e.target)) {
-                    toggleZoom();
-                }
+                if (!hasDragged && viewer.contains(e.target)) toggleZoom();
             }
         });
     }
@@ -641,36 +761,23 @@
         viewer.style.cursor = isZoomedIn ? 'grab' : 'zoom-in';
     }
     
-    // Magnifier Logic
     viewer.addEventListener('mousemove', function(e) {
-        if (!magnifierSwitch.checked) {
+        if (!magnifierSwitch.checked || isDragging || !refImage.src) {
             magnifier.style.display = 'none';
             return;
         }
-        
-        if (isDragging) {
-             magnifier.style.display = 'none';
-             return;
-        }
-        
-        if (!refImage) return;
-
         const rect = refImage.getBoundingClientRect();
         const x = e.clientX - rect.left;
         const y = e.clientY - rect.top;
-        
         if (x < 0 || x > rect.width || y < 0 || y > rect.height) {
             magnifier.style.display = 'none';
             return;
         }
-
         magnifier.style.display = 'block';
-        
         const glassOffset = 20;
         const viewerRect = viewer.getBoundingClientRect();
         magnifier.style.left = (e.clientX - viewerRect.left + glassOffset) + 'px';
         magnifier.style.top = (e.clientY - viewerRect.top + glassOffset) + 'px';
-        
         const zoom = 2.5;
         magnifier.style.backgroundImage = `url('${refImage.src}')`;
         magnifier.style.backgroundSize = `${rect.width * zoom}px ${rect.height * zoom}px`;
@@ -695,11 +802,9 @@
         currentX = 0;
         currentY = 0;
         isZoomedIn = false;
-        
         document.getElementById('brightnessRange').value = 100;
         document.getElementById('contrastRange').value = 100;
         if(magnifierSwitch) magnifierSwitch.checked = false;
-        
         updateImageTransform();
         applyImageFilters();
         if(viewer) viewer.style.cursor = 'zoom-in';
@@ -718,4 +823,4 @@
         }
     }
 </script>
-{% endblock %}
+{% endblock %}

+ 174 - 104
templates/members.html

@@ -4,113 +4,187 @@
 
 {% block extra_css %}
 <style>
-    .lineage-badge {
-        display: inline-flex;
+    .search-bar {
+        display: flex;
+        gap: var(--spacing-sm);
+        flex-wrap: wrap;
         align-items: center;
-        gap: 5px;
-        padding: 4px 14px 4px 10px;
-        border: 1.5px solid #b8c9e8;
-        border-radius: 20px;
-        background: #f0f4fb;
-        color: #3b5998;
-        font-size: 13px;
-        font-weight: 500;
-        white-space: nowrap;
-        line-height: 1.6;
     }
-    .lineage-icon {
-        width: 16px;
-        height: 16px;
-        opacity: 0.6;
-        flex-shrink: 0;
+    
+    .search-bar .input-group {
+        display: flex;
+        align-items: stretch;
+    }
+    
+    .search-bar .form-select {
+        border-radius: var(--radius-md) 0 0 var(--radius-md);
+        border-right: none;
     }
-    /* 兄弟 hover 弹窗 */
+    
+    .search-bar .form-control {
+        border-radius: 0;
+    }
+    
+    .search-bar .btn-primary {
+        border-radius: 0 var(--radius-md) var(--radius-md) 0;
+        border-left: none;
+    }
+    
     .siblings-wrapper {
         position: relative;
         display: inline-block;
     }
+    
     .siblings-popup {
         display: none;
         position: absolute;
         top: 100%;
         left: 0;
         z-index: 999;
-        background: #fff;
-        border: 1px solid #d0d7e6;
-        border-radius: 8px;
-        box-shadow: 0 4px 18px rgba(0,0,0,0.13);
-        padding: 8px 4px;
+        background: var(--color-bg-card);
+        border: 1px solid var(--color-border);
+        border-radius: var(--radius-md);
+        box-shadow: var(--shadow-lg);
+        padding: var(--spacing-xs) 0;
         min-width: 140px;
         max-width: 220px;
         max-height: 260px;
         overflow-y: auto;
         white-space: nowrap;
     }
+    
     .siblings-wrapper:hover .siblings-popup {
         display: block;
     }
+    
     .siblings-popup a {
         display: block;
-        padding: 3px 12px;
-        font-size: 12px;
-        color: #3b5998;
+        padding: var(--spacing-xs) var(--spacing-md);
+        font-size: 0.8rem;
+        color: var(--color-text-secondary);
         text-decoration: none;
-        border-radius: 4px;
-        transition: background 0.12s;
+        transition: background var(--transition-fast);
     }
+    
     .siblings-popup a:hover {
-        background: #f0f4fb;
-        color: #1a3a80;
+        background: rgba(139, 105, 20, 0.08);
+        color: var(--color-primary);
     }
+    
     .sibling-summary {
-        font-size: 11.5px;
-        color: #666;
+        font-size: 0.75rem;
+        color: var(--color-text-muted);
         margin-top: 2px;
         cursor: default;
     }
+    
     .sibling-count-badge {
         display: inline-block;
-        background: #e8eef7;
-        color: #4a69bd;
+        background: rgba(139, 105, 20, 0.1);
+        color: var(--color-primary);
         border-radius: 10px;
         padding: 0 6px;
-        font-size: 11px;
+        font-size: 0.7rem;
         vertical-align: middle;
         margin-left: 2px;
     }
+    
+    .name-word-tag {
+        display: inline-flex;
+        align-items: center;
+        gap: 3px;
+        padding: 1px 7px;
+        border-radius: 10px;
+        background: rgba(139, 105, 20, 0.1);
+        color: var(--color-primary);
+        font-size: 0.75rem;
+        border: 1px solid rgba(139, 105, 20, 0.2);
+    }
+    
+    .member-name {
+        font-family: var(--font-family-heading);
+        font-size: 1rem;
+        color: var(--color-secondary);
+    }
+    
+    .member-meta {
+        font-size: 0.8rem;
+        color: var(--color-text-muted);
+    }
+    
+    .table-action-btns {
+        display: flex;
+        gap: var(--spacing-xs);
+        justify-content: center;
+    }
+    
+    .table-action-btns .btn {
+        padding: 4px 10px;
+        font-size: 0.75rem;
+    }
+    
+    .empty-state {
+        text-align: center;
+        padding: var(--spacing-xxl);
+        color: var(--color-text-muted);
+    }
+    
+    .empty-state i {
+        font-size: 3rem;
+        margin-bottom: var(--spacing-md);
+        opacity: 0.5;
+    }
 </style>
 {% endblock %}
 
 {% block content %}
-<div class="d-flex justify-content-between align-items-center mb-4">
-    <h2><i class="bi bi-people"></i> 家谱成员管理</h2>
-    <div class="d-flex gap-2">
-        <form class="d-flex" action="{{ url_for('members') }}" method="GET">
+<div class="page-header">
+    <div class="page-title">
+        <div class="page-title-icon">
+            <i class="bi bi-people"></i>
+        </div>
+        <div>
+            <h1 class="page-title-text">成员管理</h1>
+            <p class="member-meta mt-1">管理族谱中的所有成员信息</p>
+        </div>
+    </div>
+    <div class="page-actions">
+        <div class="search-bar">
             <div class="input-group">
-                <select name="search_type" class="form-select" style="max-width:100px; border-right:0; border-radius:6px 0 0 6px;">
+                <select name="search_type" class="form-select" style="max-width:100px;">
                     <option value="name" {% if search_type != 'name_word' %}selected{% endif %}>姓名</option>
                     <option value="name_word" {% if search_type == 'name_word' %}selected{% endif %}>字辈</option>
                 </select>
                 <input type="text" name="name" class="form-control"
                        placeholder="{{ '输入字辈精确搜索...' if search_type == 'name_word' else '输入姓名搜索...' }}"
                        value="{{ search_name or '' }}">
-                <button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
-                {% if search_name %}
-                    <a href="{{ url_for('members') }}" class="btn btn-outline-secondary">重置</a>
-                {% endif %}
+                <button class="btn btn-primary" type="submit"><i class="bi bi-search"></i></button>
             </div>
-        </form>
-        <a href="{{ url_for('add_member') }}" class="btn btn-primary text-nowrap"><i class="bi bi-plus-lg"></i> 录入新成员</a>
-        <a href="{{ url_for('suspected_errors') }}" class="btn btn-warning text-nowrap"><i class="bi bi-exclamation-triangle"></i> 疑似汇总</a>
-        <a href="{{ url_for('generation_check_page') }}" class="btn btn-outline-info text-nowrap"><i class="bi bi-clipboard2-check"></i> 代数核查</a>
+            {% if search_name %}
+            <a href="{{ url_for('members') }}" class="btn btn-secondary">重置</a>
+            {% endif %}
+        </div>
+        <a href="{{ url_for('add_member') }}" class="btn btn-primary">
+            <i class="bi bi-plus-lg"></i> 录入新成员
+        </a>
+        <a href="{{ url_for('suspected_errors') }}" class="btn btn-warning">
+            <i class="bi bi-exclamation-triangle"></i> 疑似汇总
+        </a>
+        <a href="{{ url_for('generation_check_page') }}" class="btn btn-info">
+            <i class="bi bi-clipboard2-check"></i> 代数核查
+        </a>
     </div>
 </div>
 
-<div class="card shadow-sm border-0">
+<div class="card">
+    <div class="card-header">
+        <h3 class="card-header-title">成员列表</h3>
+        <span class="member-meta">共 {{ total }} 条记录</span>
+    </div>
     <div class="card-body p-0">
         <div class="table-responsive">
-            <table class="table table-hover align-middle mb-0">
-                <thead class="table-light">
+            <table class="table">
+                <thead>
                     <tr>
                         <th class="px-4">序号</th>
                         <th>基本信息</th>
@@ -129,15 +203,15 @@
                     <tr>
                         <td class="px-4 text-muted small">{{ loop.index + (page - 1) * 20 }}</td>
                         <td>
-                            <div class="fw-bold">
+                            <div class="member-name">
                                 {{ member.name }}{% if member.simplified_name %}({{ member.simplified_name }}){% endif %}
                             </div>
-                            <div class="small text-muted">
+                            <div class="member-meta">
                                 {{ '男' if member.sex == 1 else '女' }} | {{ member.birthday_str or '未知' }}
                             </div>
                             {% if member.name_word %}
                             <div class="mt-1">
-                                <span style="display:inline-flex;align-items:center;gap:3px;padding:1px 7px;border-radius:10px;background:#f0f4ff;color:#3b5bdb;font-size:11px;font-weight:500;border:1px solid #d0d9ff;">
+                                <span class="name-word-tag">
                                     <svg width="10" height="10" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
                                     字辈:{{ member.name_word }}
                                 </span>
@@ -150,7 +224,7 @@
                                 {% for gen in member.name_word_generation.split(';') %}
                                     {% if gen.strip() %}
                                     <span class="lineage-badge">
-                                        <svg class="lineage-icon" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z"/></svg>
+                                        <i class="bi bi-diagram-3"></i>
                                         {{ gen.strip() }}
                                     </span>
                                     {% endif %}
@@ -164,7 +238,7 @@
                             {% if member.father_id %}
                             {% set co_labels = {1: '长子', 2: '次子', 3: '三子', 4: '四子', 5: '五子', 6: '六子', 7: '七子', 8: '八子', 9: '九子', 10: '十子'} %}
                             <a href="{{ url_for('member_detail', member_id=member.father_id) }}"
-                               target="_blank" class="text-decoration-none small fw-medium">
+                               target="_blank" class="text-decoration-none small" style="color: var(--color-primary);">
                                 {{ member.father_name }}{% if member.father_simplified_name %}({{ member.father_simplified_name }}){% endif %}
                                 {% if member.child_order_to_father %}
                                 <span class="text-muted">{{ co_labels.get(member.child_order_to_father, '第' ~ member.child_order_to_father ~ '子') }}</span>
@@ -173,7 +247,6 @@
                             {% else %}
                             <span class="text-muted small">-</span>
                             {% endif %}
-                            {# 兄弟信息 #}
                             {% if member.siblings %}
                             {% set sibs = member.siblings %}
                             {% set first = sibs[0] %}
@@ -181,7 +254,7 @@
                             <div class="sibling-summary siblings-wrapper" style="display:block; margin-top:3px;">
                                 兄弟:<a href="{{ url_for('member_detail', member_id=first.id) }}"
                                         target="_blank"
-                                        style="font-size:11.5px; color:#3b5998; text-decoration:none;"
+                                        style="font-size:0.75rem; color: var(--color-info); text-decoration:none;"
                                         onclick="event.stopPropagation()">{{ first.simplified_name or first.name }}</a>{% if rest_count > 0 %}<span class="sibling-count-badge">+{{ rest_count }}个兄弟</span>{% endif %}
                                 {% if sibs|length > 1 %}
                                 <div class="siblings-popup">
@@ -205,43 +278,37 @@
                             {% endif %}
                         </td>
                         <td>
-                            <div class="small">
-                                {{ member.family_rank or '-' }}
-                            </div>
+                            <div class="small">{{ member.family_rank or '-' }}</div>
                         </td>
                         <td>
-                            <div class="small">
-                                {{ member.branch_family_hall or '-' }}
-                            </div>
+                            <div class="small">{{ member.branch_family_hall or '-' }}</div>
                         </td>
                         <td>
-                            <div class="small text-truncate" style="max-width: 150px;">
-                                {{ member.residential_address or '-' }}
-                            </div>
+                            <div class="small text-truncate" style="max-width: 150px;">{{ member.residential_address or '-' }}</div>
                         </td>
                         <td>
                             {% if member.is_pass_away == 1 %}
-                                <span class="badge bg-secondary">已故</span>
+                                <span class="badge badge-secondary">已故</span>
                             {% elif member.is_pass_away == 2 %}
-                                <span class="badge bg-warning">未知</span>
+                                <span class="badge badge-warning">未知</span>
                             {% else %}
-                                <span class="badge bg-success">健在</span>
+                                <span class="badge badge-success">健在</span>
                             {% endif %}
                             {% if member.modified_time_str %}
-                                <div class="text-muted" style="font-size: 0.75rem;">修改: {{ member.modified_time_str }}</div>
+                                <div class="text-muted" style="font-size: 0.7rem; margin-top: 4px;">修改: {{ member.modified_time_str }}</div>
                             {% else %}
-                                <div class="text-muted" style="font-size: 0.75rem;">录入: {{ member.create_time_str or '-' }}</div>
+                                <div class="text-muted" style="font-size: 0.7rem; margin-top: 4px;">录入: {{ member.create_time_str or '-' }}</div>
                             {% endif %}
                         </td>
                         <td class="text-center">
-                            <div class="btn-group btn-group-sm">
-                                <a href="{{ url_for('member_detail', member_id=member.id) }}" class="btn btn-outline-info">
+                            <div class="table-action-btns">
+                                <a href="{{ url_for('member_detail', member_id=member.id) }}" class="btn btn-info">
                                     <i class="bi bi-eye"></i> 详情
                                 </a>
-                                <a href="{{ url_for('edit_member', member_id=member.id) }}" class="btn btn-outline-primary">
+                                <a href="{{ url_for('edit_member', member_id=member.id) }}" class="btn btn-primary">
                                     <i class="bi bi-pencil"></i> 编辑
                                 </a>
-                                <button type="button" class="btn btn-outline-danger" onclick="confirmDelete({{ member.id }}, '{{ member.name }}')">
+                                <button type="button" class="btn btn-danger" onclick="confirmDelete({{ member.id }}, '{{ member.name }}')">
                                     <i class="bi bi-trash"></i> 删除
                                 </button>
                             </div>
@@ -250,9 +317,12 @@
                     {% endfor %}
                     {% if not members %}
                     <tr>
-                        <td colspan="9" class="text-center py-5 text-muted">
-                            <i class="bi bi-person-x fs-1 d-block mb-2"></i>
-                            暂无成员数据
+                        <td colspan="10" class="empty-state">
+                            <i class="bi bi-person-x"></i>
+                            <div>暂无成员数据</div>
+                            <a href="{{ url_for('add_member') }}" class="btn btn-primary mt-3">
+                                <i class="bi bi-plus-lg"></i> 录入新成员
+                            </a>
                         </td>
                     </tr>
                     {% endif %}
@@ -261,35 +331,35 @@
         </div>
     </div>
     
-    <!-- 分页控件 -->
     {% if total_pages > 1 %}
-    <nav class="mt-4">
-        <ul class="pagination justify-content-center">
-            <li class="page-item {{ 'disabled' if page == 1 }}">
-                <a class="page-link" href="{{ url_for('members', page=page-1, name=search_name, search_type=search_type) }}">上一页</a>
-            </li>
-            
-            {% for p in range(1, total_pages + 1) %}
-                {% if p == page %}
-                    <li class="page-item active"><span class="page-link">{{ p }}</span></li>
-                {% else %}
-                    {# Show first, last, and around current page #}
-                    {% if p == 1 or p == total_pages or (p >= page - 2 and p <= page + 2) %}
-                        <li class="page-item"><a class="page-link" href="{{ url_for('members', page=p, name=search_name, search_type=search_type) }}">{{ p }}</a></li>
-                    {% elif p == page - 3 or p == page + 3 %}
-                        <li class="page-item disabled"><span class="page-link">...</span></li>
+    <div class="card-footer bg-white border-top-0 pt-4">
+        <nav>
+            <ul class="pagination">
+                <li class="page-item {{ 'disabled' if page == 1 }}">
+                    <a class="page-link" href="{{ url_for('members', page=page-1, name=search_name, search_type=search_type) }}">上一页</a>
+                </li>
+                
+                {% for p in range(1, total_pages + 1) %}
+                    {% if p == page %}
+                        <li class="page-item active"><span class="page-link">{{ p }}</span></li>
+                    {% else %}
+                        {% if p == 1 or p == total_pages or (p >= page - 2 and p <= page + 2) %}
+                            <li class="page-item"><a class="page-link" href="{{ url_for('members', page=p, name=search_name, search_type=search_type) }}">{{ p }}</a></li>
+                        {% elif p == page - 3 or p == page + 3 %}
+                            <li class="page-item disabled"><span class="page-link">...</span></li>
+                        {% endif %}
                     {% endif %}
-                {% endif %}
-            {% endfor %}
-            
-            <li class="page-item {{ 'disabled' if page == total_pages }}">
-                <a class="page-link" href="{{ url_for('members', page=page+1, name=search_name, search_type=search_type) }}">下一页</a>
-            </li>
-        </ul>
+                {% endfor %}
+                
+                <li class="page-item {{ 'disabled' if page == total_pages }}">
+                    <a class="page-link" href="{{ url_for('members', page=page+1, name=search_name, search_type=search_type) }}">下一页</a>
+                </li>
+            </ul>
+        </nav>
         <div class="text-center text-muted small mt-2">
             共 {{ total }} 条记录,当前第 {{ page }} / {{ total_pages }} 页
         </div>
-    </nav>
+    </div>
     {% endif %}
 </div>
 {% endblock %}
@@ -306,4 +376,4 @@ function confirmDelete(id, name) {
     }
 }
 </script>
-{% endblock %}
+{% endblock %}

+ 8 - 8
templates/pdf_management.html

@@ -8,23 +8,23 @@
         cursor: pointer;
         transition: all 0.3s ease;
         border: 2px solid transparent;
-        border-radius: 12px;
+        border-radius: var(--radius-lg);
         overflow: hidden;
-        background-color: #ffffff;
-        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+        background-color: var(--color-bg-card);
+        box-shadow: var(--shadow-card);
         min-height: 280px;
         display: flex;
         flex-direction: column;
     }
     .pdf-card:hover {
-        border-color: #0d6efd;
-        box-shadow: 0 6px 20px rgba(13, 110, 253, 0.2);
+        border-color: var(--color-primary);
+        box-shadow: var(--shadow-lg);
         transform: translateY(-3px);
     }
     .pdf-card.active {
-        border-color: #0d6efd;
-        background-color: #f0f6ff;
-        box-shadow: 0 4px 16px rgba(13, 110, 253, 0.18);
+        border-color: var(--color-primary);
+        background-color: rgba(139, 105, 20, 0.05);
+        box-shadow: 0 4px 16px rgba(139, 105, 20, 0.15);
     }
     .pdf-card .card-body {
         padding: 16px;

+ 35 - 21
templates/suspected_errors.html

@@ -3,27 +3,39 @@
 {% block title %}疑似错误汇总 - 家谱管理系统{% endblock %}
 
 {% block content %}
-<div class="d-flex justify-content-between align-items-center mb-4">
-    <h2><i class="bi bi-exclamation-triangle"></i> 疑似错误汇总</h2>
-    <div class="d-flex gap-2">
-        <form class="d-flex" action="{{ url_for('suspected_errors') }}" method="GET">
+<div class="page-header">
+    <div class="page-title">
+        <div class="page-title-icon">
+            <i class="bi bi-exclamation-triangle"></i>
+        </div>
+        <div>
+            <h1 class="page-title-text">疑似错误汇总</h1>
+            <p class="member-meta mt-1">查看和管理标注了疑似错误的成员信息</p>
+        </div>
+    </div>
+    <div class="page-actions">
+        <div class="search-bar" style="display: flex; gap: var(--spacing-sm);">
             <div class="input-group">
                 <input type="text" name="name" class="form-control" placeholder="输入姓名搜索..." value="{{ search_name or '' }}">
-                <button class="btn btn-outline-primary" type="submit"><i class="bi bi-search"></i></button>
+                <button class="btn btn-primary" type="submit"><i class="bi bi-search"></i></button>
                 {% if search_name %}
-                    <a href="{{ url_for('suspected_errors') }}" class="btn btn-outline-secondary">重置</a>
+                    <a href="{{ url_for('suspected_errors') }}" class="btn btn-secondary">重置</a>
                 {% endif %}
             </div>
-        </form>
-        <a href="{{ url_for('members') }}" class="btn btn-outline-secondary"><i class="bi bi-arrow-left"></i> 返回成员列表</a>
+        </div>
+        <a href="{{ url_for('members') }}" class="btn btn-secondary"><i class="bi bi-arrow-left"></i> 返回成员列表</a>
     </div>
 </div>
 
-<div class="card shadow-sm border-0">
+<div class="card">
+    <div class="card-header">
+        <h3 class="card-header-title">疑似错误列表</h3>
+        <span class="member-meta">共 {{ total }} 条记录</span>
+    </div>
     <div class="card-body p-0">
         <div class="table-responsive">
-            <table class="table table-hover align-middle mb-0">
-                <thead class="table-light">
+            <table class="table">
+                <thead>
                     <tr>
                         <th>ID</th>
                         <th>姓名</th>
@@ -38,23 +50,26 @@
                     {% for member in members %}
                     <tr>
                         <td>{{ member.id }}</td>
-                        <td>{{ member.name }} {% if member.simplified_name and member.simplified_name != member.name %}({{ member.simplified_name }}){% endif %}</td>
+                        <td><span class="member-name">{{ member.name }} {% if member.simplified_name and member.simplified_name != member.name %}({{ member.simplified_name }}){% endif %}</span></td>
                         <td>{{ '男' if member.sex == 1 else '女' }}</td>
                         <td>{{ member.birthday_str }}</td>
                         <td>{{ member.name_word_generation or '未知' }}</td>
                         <td>
-                            <div class="text-warning">
-                                {{ member.suspected_error }}
-                            </div>
+                            <div class="text-block text-block--warning" style="padding: var(--spacing-sm);">{{ member.suspected_error }}</div>
                         </td>
                         <td>
-                            <a href="{{ url_for('member_detail', member_id=member.id) }}" class="btn btn-sm btn-primary"><i class="bi bi-eye"></i> 查看详情</a>
-                            <a href="{{ url_for('edit_member', member_id=member.id) }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-pencil"></i> 编辑</a>
+                            <div class="table-action-btns">
+                                <a href="{{ url_for('member_detail', member_id=member.id) }}" class="btn btn-info"><i class="bi bi-eye"></i> 查看详情</a>
+                                <a href="{{ url_for('edit_member', member_id=member.id) }}" class="btn btn-primary"><i class="bi bi-pencil"></i> 编辑</a>
+                            </div>
                         </td>
                     </tr>
                     {% else %}
                     <tr>
-                        <td colspan="7" class="text-center text-muted">暂无标注疑似错误的成员</td>
+                        <td colspan="7" class="empty-state">
+                            <i class="bi bi-check-circle"></i>
+                            <div>暂无标注疑似错误的成员</div>
+                        </td>
                     </tr>
                     {% endfor %}
                 </tbody>
@@ -63,9 +78,8 @@
     </div>
 </div>
 
-<!-- 分页 -->
 {% if total_pages > 1 %}
-<nav class="mt-4" aria-label="Page navigation">
+<nav class="mt-4">
     <ul class="pagination justify-content-center">
         {% if page > 1 %}
         <li class="page-item">
@@ -105,4 +119,4 @@
     </ul>
 </nav>
 {% endif %}
-{% endblock %}
+{% endblock %}

+ 5 - 5
templates/tree.html

@@ -8,11 +8,11 @@
         width: 100%; 
         height: calc(100vh - 160px); 
         min-height: 500px;
-        background: white; 
-        border: 1px solid #e9ecef; 
-        border-radius: 8px;
-        margin-top: 10px;
-        box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
+        background: var(--color-bg-card); 
+        border: 1px solid var(--color-border); 
+        border-radius: var(--radius-lg);
+        margin-top: var(--spacing-md);
+        box-shadow: var(--shadow-card);
         position: relative;
         overflow: hidden;
     }

+ 11 - 11
templates/tree_gen.html

@@ -9,13 +9,13 @@
     width: 100%;
     height: calc(100vh - 160px);
     min-height: 500px;
-    background: #fafbfd;
-    border: 1px solid #e9ecef;
-    border-radius: 8px;
+    background: var(--color-bg-card);
+    border: 1px solid var(--color-border);
+    border-radius: var(--radius-lg);
     position: relative;
     overflow: hidden;
-    margin-top: 6px;
-    box-shadow: 0 0.125rem 0.25rem rgba(0,0,0,0.075);
+    margin-top: var(--spacing-md);
+    box-shadow: var(--shadow-card);
 }
 
 /* ── 左侧世代标签栏 ── */
@@ -25,8 +25,8 @@
     width: 148px;
     height: 100%;
     z-index: 12;
-    background: #f1f5f9;
-    border-right: 1.5px solid #cbd5e1;
+    background: var(--color-bg-main);
+    border-right: 1.5px solid var(--color-border);
     overflow: hidden;
     /* pointer-events 由子元素各自控制 */
 }
@@ -34,15 +34,15 @@
     position: absolute;
     top: 0; left: 0; right: 0;
     height: 38px;
-    background: #e2e8f0;
-    border-bottom: 1px solid #cbd5e1;
+    background: var(--color-bg-main);
+    border-bottom: 1px solid var(--color-border);
     display: flex;
     align-items: center;
     justify-content: center;
     font-size: 12px;
     font-weight: 600;
-    color: #475569;
-    font-family: 'Microsoft YaHei', sans-serif;
+    color: var(--color-text-secondary);
+    font-family: var(--font-family-primary);
     z-index: 1;
     pointer-events: none;
 }

+ 255 - 75
templates/upload.html

@@ -5,79 +5,248 @@
 {% block extra_css %}
 <style>
     .drop-shadow { filter: drop-shadow(0 0 4px rgba(0,0,0,0.5)); }
+    
+    .upload-container {
+        padding: var(--spacing-lg);
+        max-width: 900px;
+        margin: 0 auto;
+    }
+    
+    .upload-card {
+        background: var(--color-bg-card);
+        border-radius: var(--radius-xl);
+        box-shadow: var(--shadow-card);
+        overflow: hidden;
+    }
+    
+    .upload-header {
+        background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark));
+        padding: var(--spacing-lg);
+        color: white;
+    }
+    
+    .upload-title {
+        font-family: var(--font-family-heading);
+        font-size: 1.25rem;
+        letter-spacing: 0.05em;
+        margin: 0;
+    }
+    
+    .upload-body {
+        padding: var(--spacing-xl);
+    }
+    
+    .form-section {
+        margin-bottom: var(--spacing-xl);
+    }
+    
+    .section-label {
+        font-family: var(--font-family-primary);
+        font-weight: 600;
+        color: var(--color-text-secondary);
+        margin-bottom: var(--spacing-md);
+        display: flex;
+        align-items: center;
+        gap: var(--spacing-sm);
+    }
+    
+    .file-input-wrapper {
+        border: 2px dashed var(--color-border);
+        border-radius: var(--radius-lg);
+        padding: var(--spacing-xl);
+        text-align: center;
+        background: var(--color-bg-main);
+        transition: all var(--transition-fast);
+        cursor: pointer;
+    }
+    
+    .file-input-wrapper:hover {
+        border-color: var(--color-primary);
+        background: rgba(139, 105, 20, 0.03);
+    }
+    
+    .file-input-wrapper.dragover {
+        border-color: var(--color-primary);
+        background: rgba(139, 105, 20, 0.08);
+        transform: scale(1.02);
+    }
+    
+    .file-input-icon {
+        font-size: 3rem;
+        color: var(--color-primary);
+        margin-bottom: var(--spacing-md);
+    }
+    
+    .file-input-text {
+        font-family: var(--font-family-primary);
+        color: var(--color-text-secondary);
+        margin-bottom: var(--spacing-sm);
+    }
+    
+    .file-input-hint {
+        font-size: 0.85rem;
+        color: var(--color-text-muted);
+    }
+    
+    .form-row {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+        gap: var(--spacing-md);
+    }
+    
+    .form-group {
+        margin-bottom: var(--spacing-md);
+    }
+    
+    .form-label {
+        font-family: var(--font-family-primary);
+        font-size: 0.85rem;
+        color: var(--color-text-secondary);
+        margin-bottom: var(--spacing-sm);
+        display: block;
+    }
+    
+    .form-control {
+        border-radius: var(--radius-md);
+        border-color: var(--color-border);
+        transition: all var(--transition-fast);
+    }
+    
+    .form-control:focus {
+        border-color: var(--color-primary);
+        box-shadow: 0 0 0 3px rgba(139, 105, 20, 0.1);
+    }
+    
+    .alert-info {
+        background: rgba(139, 105, 20, 0.08);
+        border: 1px solid rgba(139, 105, 20, 0.2);
+        border-radius: var(--radius-md);
+        color: var(--color-text-secondary);
+        padding: var(--spacing-md);
+    }
+    
+    .preview-card {
+        border-radius: var(--radius-md);
+        overflow: hidden;
+        box-shadow: var(--shadow-sm);
+    }
+    
+    .preview-img {
+        height: 200px;
+        object-fit: cover;
+        background: var(--color-bg-main);
+        transition: opacity var(--transition-fast);
+    }
+    
+    .preview-img:hover {
+        opacity: 0.8;
+    }
+    
+    .preview-click-area {
+        cursor: pointer;
+        position: relative;
+    }
+    
+    .preview-overlay {
+        position: absolute;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        color: white;
+        opacity: 0;
+        transition: opacity var(--transition-fast);
+        pointer-events: none;
+    }
+    
+    .preview-click-area:hover .preview-overlay {
+        opacity: 1;
+    }
 </style>
 {% endblock %}
 
 {% block content %}
-<div class="row justify-content-center">
-    <div class="col-md-8">
-        <div class="card shadow">
-            <div class="card-header bg-primary text-white">
-                <h5 class="mb-0"><i class="bi bi-cloud-upload me-2"></i>上传家谱扫描件</h5>
+<div class="upload-container">
+    <div class="page-header">
+        <div class="page-title">
+            <div class="page-title-icon">
+                <i class="bi bi-cloud-upload"></i>
             </div>
-            <div class="card-body p-4">
-                <form method="POST" enctype="multipart/form-data" id="uploadForm">
-                    <div class="mb-4">
-                        <label class="form-label fw-bold">选择文件</label>
-                        <input type="file" name="file" id="fileInput" class="form-control form-control-lg" multiple accept=".jpg,.jpeg,.png" required>
-                        <div class="form-text mt-2">
-                            支持图片 (JPG, PNG) 格式的扫描件。图片支持多选(一次最多10张)。上传后将自动识别或提取页码。
-                        </div>
+            <div>
+                <h1 class="page-title-text">上传家谱扫描件</h1>
+                <p class="member-meta mt-1">上传图片格式的家谱扫描件,支持批量上传</p>
+            </div>
+        </div>
+    </div>
+
+    <div class="upload-card">
+        <div class="upload-header">
+            <h2 class="upload-title"><i class="bi bi-cloud-upload me-2"></i>上传家谱扫描件</h2>
+        </div>
+        <div class="upload-body">
+            <form method="POST" enctype="multipart/form-data" id="uploadForm">
+                <div class="form-section">
+                    <label class="section-label">选择文件</label>
+                    <div class="file-input-wrapper" id="fileDropArea">
+                        <input type="file" name="file" id="fileInput" class="form-control" multiple accept=".jpg,.jpeg,.png" required style="display: none;">
+                        <div class="file-input-icon"><i class="bi bi-images"></i></div>
+                        <div class="file-input-text">点击或拖拽文件到此处上传</div>
+                        <div class="file-input-hint">支持图片 (JPG, PNG) 格式的扫描件。图片支持多选(一次最多10张)。上传后将自动识别或提取页码。</div>
                     </div>
+                </div>
 
-                    <div class="row mb-4">
-                        <div class="col-md-4">
-                            <label class="form-label fw-bold">版本名称 <span class="text-danger">*</span></label>
+                <div class="form-section">
+                    <label class="section-label">基本信息</label>
+                    <div class="form-row">
+                        <div class="form-group">
+                            <label class="form-label">版本名称 <span class="text-danger">*</span></label>
                             <input type="text" name="genealogy_version" class="form-control" placeholder="如:衢州1926版" required>
                         </div>
-                        <div class="col-md-4">
-                            <label class="form-label fw-bold">版本来源 <span class="text-danger">*</span></label>
+                        <div class="form-group">
+                            <label class="form-label">版本来源 <span class="text-danger">*</span></label>
                             <input type="text" name="genealogy_source" class="form-control" placeholder="如:留越收藏" required>
                         </div>
-                        <div class="col-md-4">
-                            <label class="form-label fw-bold">文件提供人</label>
+                        <div class="form-group">
+                            <label class="form-label">文件提供人</label>
                             <input type="text" name="upload_person" class="form-control" placeholder="默认:当前系统登录账号">
                         </div>
                     </div>
+                </div>
 
-                    <div class="mb-4" id="globalPageWrapper">
-                        <label class="form-label fw-bold">手动指定页码 (可选)</label>
-                        <div class="input-group">
-                            <input type="number" name="manual_page" id="initialPage" class="form-control" placeholder="如不输入则由 OCR 自动识别">
-                            <button class="btn btn-outline-secondary" type="button" onclick="document.getElementById('initialPage').value = {{ suggested_page }}">
-                                提示:第 {{ suggested_page }} 页?
-                            </button>
-                        </div>
-                        <div class="form-text mt-1 text-muted">
-                            建议值为当前数据库最大页码 + 1。
-                        </div>
+                <div class="form-section" id="globalPageWrapper">
+                    <label class="section-label">手动指定页码 (可选)</label>
+                    <div class="input-group">
+                        <input type="number" name="manual_page" id="initialPage" class="form-control" placeholder="如不输入则由 OCR 自动识别">
+                        <button class="btn btn-secondary" type="button" onclick="document.getElementById('initialPage').value = {{ suggested_page }}">
+                            提示:第 {{ suggested_page }} 页?
+                        </button>
                     </div>
-                    
-                    <!-- 预览容器 -->
-                    <div id="previewContainer" class="mb-4" style="display: none;">
-                        <label class="form-label fw-bold">文件预览及页码设置</label>
-                        <div class="row g-3" id="previewGrid">
-                        </div>
+                    <div class="form-text mt-1" style="color: var(--color-text-muted);">
+                        建议值为当前数据库最大页码 + 1。
                     </div>
-                    
-                    <div class="alert alert-warning mb-4">
-                        <i class="bi bi-info-circle me-2"></i>
-                        提示:文件将上传至云端 OSS 存储,处理过程可能需要几秒钟。
+                </div>
+                
+                <div id="previewContainer" class="form-section" style="display: none;">
+                    <label class="section-label">文件预览及页码设置</label>
+                    <div class="row g-3" id="previewGrid">
                     </div>
+                </div>
+                
+                <div class="alert-info">
+                    <i class="bi bi-info-circle me-2"></i>
+                    提示:文件将上传至云端 OSS 存储,处理过程可能需要几秒钟。
+                </div>
 
-                    <div class="d-grid gap-2 d-md-flex justify-content-md-end">
-                        <a href="{{ url_for('index') }}" class="btn btn-light px-4">取消</a>
-                        <button type="submit" class="btn btn-primary px-5" id="submitBtn">
-                            <i class="bi bi-check-lg me-1"></i> 开始上传
-                        </button>
-                    </div>
-                </form>
-            </div>
+                <div class="d-flex gap-2 justify-content-end">
+                    <a href="{{ url_for('index') }}" class="btn btn-secondary">取消</a>
+                    <button type="submit" class="btn btn-primary btn-lg" id="submitBtn">
+                        <i class="bi bi-check-lg me-1"></i> 开始上传
+                    </button>
+                </div>
+            </form>
         </div>
     </div>
 </div>
 
-<!-- 放大查看图片的模态框 -->
 <div class="modal fade" id="imagePreviewModal" tabindex="-1" aria-hidden="true" style="z-index: 1060;">
     <div class="modal-dialog modal-xl modal-dialog-centered" style="max-width: 95vw;">
         <div class="modal-content bg-transparent border-0">
@@ -87,7 +256,6 @@
             <div class="modal-body p-0 text-center position-relative d-flex justify-content-center align-items-center" style="min-height: 50vh;">
                 <img id="largePreviewImage" src="" class="img-fluid rounded shadow-lg" style="max-height: 90vh; max-width: 100%; object-fit: contain; pointer-events: none;">
                 
-                <!-- 切换按钮 -->
                 <button class="btn btn-dark position-absolute top-50 start-0 translate-middle-y ms-3 ms-md-5 rounded-circle shadow" id="prevImageBtn" style="width: 48px; height: 48px; opacity: 0.8; z-index: 1065;">
                     <i class="bi bi-chevron-left fs-4"></i>
                 </button>
@@ -95,7 +263,6 @@
                     <i class="bi bi-chevron-right fs-4"></i>
                 </button>
                 
-                <!-- 页码指示器 -->
                 <div class="position-absolute bottom-0 start-50 translate-middle-x mb-3" style="z-index: 1065;">
                     <span class="badge bg-dark bg-opacity-75 fs-6 px-3 py-2 rounded-pill shadow" id="imageIndexIndicator">1 / 1</span>
                 </div>
@@ -108,27 +275,46 @@
 {% block extra_js %}
 <script>
     document.getElementById('uploadForm').addEventListener('submit', function(e) {
-        // Prevent multiple submissions
         const btn = document.getElementById('submitBtn');
         if (btn.classList.contains('disabled')) {
             e.preventDefault();
             return;
         }
         
-        // Show loading state without blocking submit
         btn.classList.add('disabled');
         btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 上传处理中...';
         
-        // Disable after a tiny delay to ensure form submission proceeds
         setTimeout(() => {
             btn.disabled = true;
         }, 10);
     });
 
+    const fileDropArea = document.getElementById('fileDropArea');
+    const fileInput = document.getElementById('fileInput');
+
+    fileDropArea.addEventListener('click', () => fileInput.click());
+
+    fileDropArea.addEventListener('dragover', (e) => {
+        e.preventDefault();
+        fileDropArea.classList.add('dragover');
+    });
+
+    fileDropArea.addEventListener('dragleave', () => {
+        fileDropArea.classList.remove('dragover');
+    });
+
+    fileDropArea.addEventListener('drop', (e) => {
+        e.preventDefault();
+        fileDropArea.classList.remove('dragover');
+        if (e.dataTransfer.files.length > 0) {
+            fileInput.files = e.dataTransfer.files;
+            fileInput.dispatchEvent(new Event('change'));
+        }
+    });
+
     let currentPreviewFiles = [];
     let currentPreviewIndex = 0;
     
-    // 暴露函数到全局以确保 onclick 能找到
     window.showLargePreview = function(index) {
         if (currentPreviewFiles.length === 0 || index < 0 || index >= currentPreviewFiles.length) {
             return;
@@ -143,22 +329,19 @@
         
         document.getElementById('imageIndexIndicator').textContent = `${index + 1} / ${currentPreviewFiles.length}`;
         
-        // Update button states
         document.getElementById('prevImageBtn').style.display = index === 0 ? 'none' : 'block';
         document.getElementById('nextImageBtn').style.display = index === currentPreviewFiles.length - 1 ? 'none' : 'block';
         
-        // 使用原生 Bootstrap API 显示模态框
         const modalEl = document.getElementById('imagePreviewModal');
         let modal = bootstrap.Modal.getInstance(modalEl);
         if (!modal) {
             modal = new bootstrap.Modal(modalEl);
         }
         
-        // Ensure image fits vertically
         imgEl.style.maxHeight = '85vh';
         imgEl.style.maxWidth = '100%';
         imgEl.style.objectFit = 'contain';
-        imgEl.style.pointerEvents = 'auto'; // allow user interaction if needed
+        imgEl.style.pointerEvents = 'auto';
         
         modal.show();
     };
@@ -173,7 +356,6 @@
         if (currentPreviewIndex < currentPreviewFiles.length - 1) window.showLargePreview(currentPreviewIndex + 1);
     });
     
-    // Keyboard navigation
     document.getElementById('imagePreviewModal').addEventListener('keydown', (e) => {
         if (e.key === 'ArrowLeft') {
             if (currentPreviewIndex > 0) window.showLargePreview(currentPreviewIndex - 1);
@@ -191,7 +373,7 @@
                 imgCount++;
             } else if (files[i].name.toLowerCase().endsWith('.pdf')) {
                 alert('扫描件管理仅支持图片上传,请在家谱管理中上传PDF文件。');
-                e.target.value = ''; // clear selection
+                e.target.value = '';
                 document.getElementById('previewContainer').style.display = 'none';
                 document.getElementById('globalPageWrapper').style.display = 'block';
                 return;
@@ -200,7 +382,7 @@
 
         if (imgCount > 10) {
             alert('一次最多只能上传10张图片,请重新选择。');
-            e.target.value = ''; // clear selection
+            e.target.value = '';
             document.getElementById('previewContainer').style.display = 'none';
             document.getElementById('globalPageWrapper').style.display = 'block';
             return;
@@ -214,12 +396,11 @@
         
         if (files.length > 0) {
             previewContainer.style.display = 'block';
-            // Hide global page input for images, as we will set them individually
             globalPageWrapper.style.display = 'none';
             
             let startPage = parseInt('{{ suggested_page }}') || 1;
             
-            currentPreviewFiles = []; // Reset current preview files
+            currentPreviewFiles = [];
             
             Array.from(files).forEach((file, index) => {
                 if (file.type.startsWith('image/')) {
@@ -231,31 +412,30 @@
 
             Array.from(files).forEach((file, index) => {
                 const isImage = file.type.startsWith('image/');
-                if (!isImage) return; // Skip non-images
+                if (!isImage) return;
                 
                 const currentIndex = imageIndexCounter++;
                 const objectUrl = URL.createObjectURL(file);
                 const col = document.createElement('div');
                 col.className = 'col-md-6 col-lg-4';
                 col.innerHTML = `
-                    <div class="card h-100">
-                        <div class="position-relative preview-click-area" data-index="${currentIndex}" style="cursor: pointer;">
-                            <img src="${objectUrl}" class="card-img-top" style="height: 200px; object-fit: cover; background: #f8f9fa; transition: opacity 0.2s;" alt="预览图" onmouseover="this.style.opacity='0.8'" onmouseout="this.style.opacity='1'">
-                            <div class="position-absolute top-50 start-50 translate-middle text-white" style="opacity: 0; transition: opacity 0.2s; pointer-events: none;" onmouseover="this.parentElement.querySelector('img').style.opacity='0.8'; this.style.opacity='1'" onmouseout="this.parentElement.querySelector('img').style.opacity='1'; this.style.opacity='0'">
+                    <div class="preview-card h-100">
+                        <div class="preview-click-area" data-index="${currentIndex}">
+                            <img src="${objectUrl}" class="preview-img w-full" alt="预览图">
+                            <div class="preview-overlay">
                                 <i class="bi bi-zoom-in fs-1 drop-shadow"></i>
                             </div>
                         </div>
-                        <div class="card-body p-2 border-top">
-                            <div class="text-truncate small mb-2 text-muted" title="${file.name}">${file.name}</div>
+                        <div class="p-2 border-top">
+                            <div class="text-truncate small mb-2" style="color: var(--color-text-muted);" title="${file.name}">${file.name}</div>
                             <div class="input-group input-group-sm">
-                                <span class="input-group-text bg-light text-secondary">指定页码</span>
+                                <span class="input-group-text" style="background: var(--color-bg-main); color: var(--color-text-secondary);">指定页码</span>
                                 <input type="number" name="page_number_${index}" class="form-control" value="${startPage + currentIndex}">
                             </div>
                         </div>
                     </div>
                 `;
                 
-                // Bind click event safely
                 const clickArea = col.querySelector('.preview-click-area');
                 if (clickArea) {
                     clickArea.addEventListener('click', function(evt) {
@@ -273,4 +453,4 @@
         }
     });
 </script>
-{% endblock %}
+{% endblock %}

Неке датотеке нису приказане због велике количине промена