|
@@ -4,88 +4,138 @@
|
|
|
|
|
|
|
|
{% block extra_css %}
|
|
{% block extra_css %}
|
|
|
<style>
|
|
<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 {
|
|
.source-image-preview {
|
|
|
cursor: pointer;
|
|
cursor: pointer;
|
|
|
position: relative;
|
|
position: relative;
|
|
|
overflow: hidden;
|
|
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 {
|
|
.source-image-preview:hover {
|
|
|
transform: scale(1.02);
|
|
transform: scale(1.02);
|
|
|
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
|
|
|
|
|
|
+ box-shadow: var(--shadow-md);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.source-image-preview img {
|
|
.source-image-preview img {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
height: auto;
|
|
height: auto;
|
|
|
display: block;
|
|
display: block;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.preview-overlay {
|
|
.preview-overlay {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
bottom: 0;
|
|
bottom: 0;
|
|
|
left: 0;
|
|
left: 0;
|
|
|
right: 0;
|
|
right: 0;
|
|
|
- background: rgba(0,0,0,0.6);
|
|
|
|
|
|
|
+ background: rgba(44, 24, 16, 0.6);
|
|
|
color: white;
|
|
color: white;
|
|
|
- padding: 5px;
|
|
|
|
|
|
|
+ padding: var(--spacing-sm);
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
- font-size: 0.8rem;
|
|
|
|
|
|
|
+ font-size: 0.75rem;
|
|
|
opacity: 0;
|
|
opacity: 0;
|
|
|
- transition: opacity 0.2s;
|
|
|
|
|
|
|
+ transition: opacity var(--transition-fast);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.source-image-preview:hover .preview-overlay {
|
|
.source-image-preview:hover .preview-overlay {
|
|
|
opacity: 1;
|
|
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 {
|
|
.detail-preview-empty {
|
|
|
min-height: 160px;
|
|
min-height: 160px;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
justify-content: 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 {
|
|
.modal-image-container {
|
|
|
height: 80vh;
|
|
height: 80vh;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.image-toolbar {
|
|
.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;
|
|
display: flex;
|
|
|
- gap: 10px;
|
|
|
|
|
|
|
+ gap: var(--spacing-md);
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
flex-wrap: wrap;
|
|
flex-wrap: wrap;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.image-viewer {
|
|
.image-viewer {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
- border: 1px solid #ccc;
|
|
|
|
|
- background: #f0f0f0;
|
|
|
|
|
|
|
+ border: 1px solid var(--color-border);
|
|
|
|
|
+ background: var(--color-bg-main);
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
position: relative;
|
|
position: relative;
|
|
|
cursor: grab;
|
|
cursor: grab;
|
|
|
user-select: none;
|
|
user-select: none;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.image-viewer:active {
|
|
.image-viewer:active {
|
|
|
cursor: grabbing;
|
|
cursor: grabbing;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.image-wrapper {
|
|
.image-wrapper {
|
|
|
display: inline-block;
|
|
display: inline-block;
|
|
|
transition: transform 0.2s ease-out;
|
|
transition: transform 0.2s ease-out;
|
|
@@ -95,125 +145,225 @@
|
|
|
left: 50%;
|
|
left: 50%;
|
|
|
transform: translate(-50%, -50%);
|
|
transform: translate(-50%, -50%);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.image-wrapper img {
|
|
.image-wrapper img {
|
|
|
max-width: 100%;
|
|
max-width: 100%;
|
|
|
- max-height: 100%; /* Fit within viewer initially */
|
|
|
|
|
|
|
+ max-height: 100%;
|
|
|
display: block;
|
|
display: block;
|
|
|
pointer-events: none;
|
|
pointer-events: none;
|
|
|
user-select: none;
|
|
user-select: none;
|
|
|
transition: filter 0.2s;
|
|
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 {
|
|
.magnifier-glass {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
- border: 3px solid #000;
|
|
|
|
|
|
|
+ border: 3px solid var(--color-primary);
|
|
|
border-radius: 50%;
|
|
border-radius: 50%;
|
|
|
cursor: none;
|
|
cursor: none;
|
|
|
width: 150px;
|
|
width: 150px;
|
|
|
height: 150px;
|
|
height: 150px;
|
|
|
- box-shadow: 0 0 10px rgba(0,0,0,0.5);
|
|
|
|
|
|
|
+ box-shadow: var(--shadow-lg);
|
|
|
display: none;
|
|
display: none;
|
|
|
z-index: 1000;
|
|
z-index: 1000;
|
|
|
background-repeat: no-repeat;
|
|
background-repeat: no-repeat;
|
|
|
background-color: white;
|
|
background-color: white;
|
|
|
pointer-events: none;
|
|
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>
|
|
</style>
|
|
|
{% endblock %}
|
|
{% endblock %}
|
|
|
|
|
|
|
|
{% block content %}
|
|
{% 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>
|
|
</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-label">姓名(繁体):</span>
|
|
|
<span class="info-value">{{ member.name }}</span>
|
|
<span class="info-value">{{ member.name }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">姓名(简体):</span>
|
|
<span class="info-label">姓名(简体):</span>
|
|
|
<span class="info-value">{{ member.simplified_name or '-' }}</span>
|
|
<span class="info-value">{{ member.simplified_name or '-' }}</span>
|
|
|
</div>
|
|
</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-label">性别:</span>
|
|
|
<span class="info-value">{{ '男' if member.sex == 1 else '女' }}</span>
|
|
<span class="info-value">{{ '男' if member.sex == 1 else '女' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">曾用名:</span>
|
|
<span class="info-label">曾用名:</span>
|
|
|
<span class="info-value">{{ member.former_name or '-' }}</span>
|
|
<span class="info-value">{{ member.former_name or '-' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">幼名/乳名:</span>
|
|
<span class="info-label">幼名/乳名:</span>
|
|
|
<span class="info-value">{{ member.childhood_name or '-' }}</span>
|
|
<span class="info-value">{{ member.childhood_name or '-' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">出生日期:</span>
|
|
<span class="info-label">出生日期:</span>
|
|
|
<span class="info-value">{{ member.birthday_str or '未知' }}</span>
|
|
<span class="info-value">{{ member.birthday_str or '未知' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6">
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">民族:</span>
|
|
<span class="info-label">民族:</span>
|
|
|
<span class="info-value">{{ member.nation or '-' }}</span>
|
|
<span class="info-value">{{ member.nation or '-' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">状态:</span>
|
|
<span class="info-label">状态:</span>
|
|
|
<span class="info-value">
|
|
<span class="info-value">
|
|
|
{% if member.is_pass_away == 1 %}
|
|
{% if member.is_pass_away == 1 %}
|
|
|
- <span class="text-danger">已故</span>
|
|
|
|
|
|
|
+ <span class="badge badge-secondary">已故</span>
|
|
|
{% elif member.is_pass_away == 2 %}
|
|
{% elif member.is_pass_away == 2 %}
|
|
|
- <span class="text-warning">未知</span>
|
|
|
|
|
|
|
+ <span class="badge badge-warning">未知</span>
|
|
|
{% else %}
|
|
{% else %}
|
|
|
- <span class="text-success">健在</span>
|
|
|
|
|
|
|
+ <span class="badge badge-success">健在</span>
|
|
|
{% endif %}
|
|
{% endif %}
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">婚姻状况:</span>
|
|
<span class="info-label">婚姻状况:</span>
|
|
|
<span class="info-value">
|
|
<span class="info-value">
|
|
|
{% set marital_map = {0: '未知', 1: '未婚', 2: '已婚', 3: '离异/丧偶'} %}
|
|
{% set marital_map = {0: '未知', 1: '未婚', 2: '已婚', 3: '离异/丧偶'} %}
|
|
|
{{ marital_map.get(member.marital_status, '未知') }}
|
|
{{ marital_map.get(member.marital_status, '未知') }}
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</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>
|
|
|
|
|
|
|
|
- <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-label">字辈:</span>
|
|
|
<span class="info-value">{{ member.name_word or '-' }}</span>
|
|
<span class="info-value">{{ member.name_word or '-' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">堂内排行:</span>
|
|
<span class="info-label">堂内排行:</span>
|
|
|
<span class="info-value">{{ member.family_rank or '-' }}</span>
|
|
<span class="info-value">{{ member.family_rank or '-' }}</span>
|
|
|
</div>
|
|
</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-label">世系世代:</span>
|
|
|
<span class="info-value">
|
|
<span class="info-value">
|
|
|
{% if member.name_word_generation %}
|
|
{% if member.name_word_generation %}
|
|
@@ -221,8 +371,8 @@
|
|
|
<span class="d-inline-flex flex-wrap gap-1 align-items-center">
|
|
<span class="d-inline-flex flex-wrap gap-1 align-items-center">
|
|
|
{% for gen in generations %}
|
|
{% for gen in generations %}
|
|
|
{% if gen.strip() %}
|
|
{% 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>
|
|
</span>
|
|
|
{% endif %}
|
|
{% endif %}
|
|
|
{% endfor %}
|
|
{% endfor %}
|
|
@@ -232,194 +382,185 @@
|
|
|
{% endif %}
|
|
{% endif %}
|
|
|
</span>
|
|
</span>
|
|
|
</div>
|
|
</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-label">聚居地:</span>
|
|
|
<span class="info-value">{{ member.cluster_place or '-' }}</span>
|
|
<span class="info-value">{{ member.cluster_place or '-' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
</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-label">手机号:</span>
|
|
|
<span class="info-value">{{ member.phone or '-' }}</span>
|
|
<span class="info-value">{{ member.phone or '-' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-6">
|
|
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">微信号:</span>
|
|
<span class="info-label">微信号:</span>
|
|
|
<span class="info-value">{{ member.wechat_account or '-' }}</span>
|
|
<span class="info-value">{{ member.wechat_account or '-' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="col-md-12">
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6">
|
|
|
|
|
+ <div class="info-row">
|
|
|
<span class="info-label">现居住址:</span>
|
|
<span class="info-label">现居住址:</span>
|
|
|
<span class="info-value">{{ member.residential_address or '-' }}</span>
|
|
<span class="info-value">{{ member.residential_address or '-' }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</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>
|
|
|
- <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>
|
|
</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>
|
|
</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>
|
|
</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>
|
|
</div>
|
|
|
|
|
+ {% endfor %}
|
|
|
|
|
+ {% if not parents %}
|
|
|
|
|
+ <p class="text-muted small">暂无上层关系记录</p>
|
|
|
{% endif %}
|
|
{% 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>
|
|
|
</div>
|
|
</div>
|
|
|
- {% endfor %}
|
|
|
|
|
- {% if not children %}
|
|
|
|
|
- <p class="text-muted small">暂无下层关系记录</p>
|
|
|
|
|
- {% endif %}
|
|
|
|
|
</div>
|
|
</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>
|
|
</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>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
-<!-- Image Viewer Modal -->
|
|
|
|
|
<div class="modal fade" id="imageModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal fade" id="imageModal" tabindex="-1" aria-hidden="true">
|
|
|
<div class="modal-dialog modal-fullscreen">
|
|
<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>
|
|
<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>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
|
</div>
|
|
</div>
|
|
@@ -427,19 +568,19 @@
|
|
|
<ul class="nav nav-tabs px-3 pt-2 bg-white border-bottom" id="modalImageTabNav">
|
|
<ul class="nav nav-tabs px-3 pt-2 bg-white border-bottom" id="modalImageTabNav">
|
|
|
<li class="nav-item">
|
|
<li class="nav-item">
|
|
|
<button type="button" class="nav-link" id="modal-tab-scan" onclick="switchModalImageTab('scan')">
|
|
<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>
|
|
</button>
|
|
|
</li>
|
|
</li>
|
|
|
<li class="nav-item">
|
|
<li class="nav-item">
|
|
|
<button type="button" class="nav-link" id="modal-tab-reference" onclick="switchModalImageTab('reference')">
|
|
<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>
|
|
</button>
|
|
|
</li>
|
|
</li>
|
|
|
</ul>
|
|
</ul>
|
|
|
<div class="image-toolbar">
|
|
<div class="image-toolbar">
|
|
|
<div class="btn-group btn-group-sm">
|
|
<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>
|
|
|
<div class="d-flex align-items-center gap-2 mx-3 border-start border-end px-3">
|
|
<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>
|
|
<i class="bi bi-brightness-high" title="亮度"></i>
|
|
@@ -453,11 +594,11 @@
|
|
|
<label class="form-check-label small" for="magnifierSwitch">🔍 放大镜</label>
|
|
<label class="form-check-label small" for="magnifierSwitch">🔍 放大镜</label>
|
|
|
</div>
|
|
</div>
|
|
|
</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="magnifier" class="magnifier-glass"></div>
|
|
|
<div id="imageWrapper" class="image-wrapper">
|
|
<div id="imageWrapper" class="image-wrapper">
|
|
|
<img id="refImage" src="" alt="家谱图片" draggable="false" style="display: none;">
|
|
<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>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -531,7 +672,7 @@
|
|
|
} else {
|
|
} else {
|
|
|
img.style.display = 'none';
|
|
img.style.display = 'none';
|
|
|
img.removeAttribute('src');
|
|
img.removeAttribute('src');
|
|
|
- emptyState.style.display = 'block';
|
|
|
|
|
|
|
+ emptyState.style.display = 'flex';
|
|
|
emptyState.textContent = modalActiveTab === 'scan' ? '暂无扫描件' : '暂无参考件';
|
|
emptyState.textContent = modalActiveTab === 'scan' ? '暂无扫描件' : '暂无参考件';
|
|
|
title.textContent = '家谱图片查看';
|
|
title.textContent = '家谱图片查看';
|
|
|
}
|
|
}
|
|
@@ -542,7 +683,7 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
- if (document.getElementById('detailImageTabNav')) {
|
|
|
|
|
|
|
+ if (document.getElementById('detail-tab-scan')) {
|
|
|
switchDetailImageTab(detailActiveTab);
|
|
switchDetailImageTab(detailActiveTab);
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -560,33 +701,23 @@
|
|
|
myModal.show();
|
|
myModal.show();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // --- Image Viewer Logic (Reused from add_member) ---
|
|
|
|
|
- // Image State
|
|
|
|
|
let imgRotation = 0;
|
|
let imgRotation = 0;
|
|
|
let imgBrightness = 100;
|
|
let imgBrightness = 100;
|
|
|
let imgContrast = 100;
|
|
let imgContrast = 100;
|
|
|
-
|
|
|
|
|
- // Dragging State
|
|
|
|
|
let isDragging = false;
|
|
let isDragging = false;
|
|
|
let hasDragged = false;
|
|
let hasDragged = false;
|
|
|
let startX = 0, startY = 0;
|
|
let startX = 0, startY = 0;
|
|
|
- let currentX = 0, currentY = 0; // Relative to center
|
|
|
|
|
-
|
|
|
|
|
- // Zoom State
|
|
|
|
|
|
|
+ let currentX = 0, currentY = 0;
|
|
|
let isZoomedIn = false;
|
|
let isZoomedIn = false;
|
|
|
const ZOOM_LEVEL = 2.0;
|
|
const ZOOM_LEVEL = 2.0;
|
|
|
|
|
|
|
|
- // Elements
|
|
|
|
|
const viewer = document.getElementById('viewer');
|
|
const viewer = document.getElementById('viewer');
|
|
|
const magnifier = document.getElementById('magnifier');
|
|
const magnifier = document.getElementById('magnifier');
|
|
|
const magnifierSwitch = document.getElementById('magnifierSwitch');
|
|
const magnifierSwitch = document.getElementById('magnifierSwitch');
|
|
|
const imageWrapper = document.getElementById('imageWrapper');
|
|
const imageWrapper = document.getElementById('imageWrapper');
|
|
|
const refImage = document.getElementById('refImage');
|
|
const refImage = document.getElementById('refImage');
|
|
|
|
|
|
|
|
- // Initialize Dragging and Zooming
|
|
|
|
|
if (imageWrapper) {
|
|
if (imageWrapper) {
|
|
|
- // Center initial position logic is handled by CSS (top 50% left 50% translate -50%)
|
|
|
|
|
-
|
|
|
|
|
viewer.style.cursor = 'zoom-in';
|
|
viewer.style.cursor = 'zoom-in';
|
|
|
|
|
|
|
|
viewer.addEventListener('mousedown', (e) => {
|
|
viewer.addEventListener('mousedown', (e) => {
|
|
@@ -595,27 +726,19 @@
|
|
|
hasDragged = false;
|
|
hasDragged = false;
|
|
|
startX = e.clientX;
|
|
startX = e.clientX;
|
|
|
startY = e.clientY;
|
|
startY = e.clientY;
|
|
|
-
|
|
|
|
|
viewer.style.cursor = 'grabbing';
|
|
viewer.style.cursor = 'grabbing';
|
|
|
e.preventDefault();
|
|
e.preventDefault();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
window.addEventListener('mousemove', (e) => {
|
|
window.addEventListener('mousemove', (e) => {
|
|
|
if (!isDragging) return;
|
|
if (!isDragging) return;
|
|
|
-
|
|
|
|
|
const dx = e.clientX - startX;
|
|
const dx = e.clientX - startX;
|
|
|
const dy = e.clientY - startY;
|
|
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;
|
|
currentX += dx;
|
|
|
currentY += dy;
|
|
currentY += dy;
|
|
|
-
|
|
|
|
|
startX = e.clientX;
|
|
startX = e.clientX;
|
|
|
startY = e.clientY;
|
|
startY = e.clientY;
|
|
|
-
|
|
|
|
|
updateImageTransform();
|
|
updateImageTransform();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -623,10 +746,7 @@
|
|
|
if (isDragging) {
|
|
if (isDragging) {
|
|
|
isDragging = false;
|
|
isDragging = false;
|
|
|
viewer.style.cursor = isZoomedIn ? 'grab' : 'zoom-in';
|
|
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';
|
|
viewer.style.cursor = isZoomedIn ? 'grab' : 'zoom-in';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Magnifier Logic
|
|
|
|
|
viewer.addEventListener('mousemove', function(e) {
|
|
viewer.addEventListener('mousemove', function(e) {
|
|
|
- if (!magnifierSwitch.checked) {
|
|
|
|
|
|
|
+ if (!magnifierSwitch.checked || isDragging || !refImage.src) {
|
|
|
magnifier.style.display = 'none';
|
|
magnifier.style.display = 'none';
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- if (isDragging) {
|
|
|
|
|
- magnifier.style.display = 'none';
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- if (!refImage) return;
|
|
|
|
|
-
|
|
|
|
|
const rect = refImage.getBoundingClientRect();
|
|
const rect = refImage.getBoundingClientRect();
|
|
|
const x = e.clientX - rect.left;
|
|
const x = e.clientX - rect.left;
|
|
|
const y = e.clientY - rect.top;
|
|
const y = e.clientY - rect.top;
|
|
|
-
|
|
|
|
|
if (x < 0 || x > rect.width || y < 0 || y > rect.height) {
|
|
if (x < 0 || x > rect.width || y < 0 || y > rect.height) {
|
|
|
magnifier.style.display = 'none';
|
|
magnifier.style.display = 'none';
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
magnifier.style.display = 'block';
|
|
magnifier.style.display = 'block';
|
|
|
-
|
|
|
|
|
const glassOffset = 20;
|
|
const glassOffset = 20;
|
|
|
const viewerRect = viewer.getBoundingClientRect();
|
|
const viewerRect = viewer.getBoundingClientRect();
|
|
|
magnifier.style.left = (e.clientX - viewerRect.left + glassOffset) + 'px';
|
|
magnifier.style.left = (e.clientX - viewerRect.left + glassOffset) + 'px';
|
|
|
magnifier.style.top = (e.clientY - viewerRect.top + glassOffset) + 'px';
|
|
magnifier.style.top = (e.clientY - viewerRect.top + glassOffset) + 'px';
|
|
|
-
|
|
|
|
|
const zoom = 2.5;
|
|
const zoom = 2.5;
|
|
|
magnifier.style.backgroundImage = `url('${refImage.src}')`;
|
|
magnifier.style.backgroundImage = `url('${refImage.src}')`;
|
|
|
magnifier.style.backgroundSize = `${rect.width * zoom}px ${rect.height * zoom}px`;
|
|
magnifier.style.backgroundSize = `${rect.width * zoom}px ${rect.height * zoom}px`;
|
|
@@ -695,11 +802,9 @@
|
|
|
currentX = 0;
|
|
currentX = 0;
|
|
|
currentY = 0;
|
|
currentY = 0;
|
|
|
isZoomedIn = false;
|
|
isZoomedIn = false;
|
|
|
-
|
|
|
|
|
document.getElementById('brightnessRange').value = 100;
|
|
document.getElementById('brightnessRange').value = 100;
|
|
|
document.getElementById('contrastRange').value = 100;
|
|
document.getElementById('contrastRange').value = 100;
|
|
|
if(magnifierSwitch) magnifierSwitch.checked = false;
|
|
if(magnifierSwitch) magnifierSwitch.checked = false;
|
|
|
-
|
|
|
|
|
updateImageTransform();
|
|
updateImageTransform();
|
|
|
applyImageFilters();
|
|
applyImageFilters();
|
|
|
if(viewer) viewer.style.cursor = 'zoom-in';
|
|
if(viewer) viewer.style.cursor = 'zoom-in';
|
|
@@ -718,4 +823,4 @@
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
-{% endblock %}
|
|
|
|
|
|
|
+{% endblock %}
|