Răsfoiți Sursa

commit 优化

Hai Lin 3 săptămâni în urmă
părinte
comite
c694d2bfa7
2 a modificat fișierele cu 315 adăugiri și 12 ștergeri
  1. 69 4
      app.py
  2. 246 8
      templates/add_member.html

+ 69 - 4
app.py

@@ -800,6 +800,60 @@ def save_relation():
     finally:
     finally:
         conn.close()
         conn.close()
 
 
+@app.route('/manager/api/members')
+def get_members():
+    if 'user_id' not in session:
+        return jsonify({"success": False, "message": "Unauthorized"}), 401
+    
+    page = int(request.args.get('page', 1))
+    search = request.args.get('search', '')
+    per_page = 10
+    offset = (page - 1) * per_page
+    
+    conn = get_db_connection()
+    try:
+        with conn.cursor() as cursor:
+            # Count total members
+            if search:
+                cursor.execute("SELECT COUNT(*) as total FROM family_member_info WHERE name LIKE %s OR simplified_name LIKE %s", 
+                              (f'%{search}%', f'%{search}%'))
+            else:
+                cursor.execute("SELECT COUNT(*) as total FROM family_member_info")
+            total = cursor.fetchone()['total']
+            
+            # Get members for current page
+            if search:
+                cursor.execute("SELECT id, name, simplified_name, sex FROM family_member_info WHERE name LIKE %s OR simplified_name LIKE %s LIMIT %s OFFSET %s", 
+                              (f'%{search}%', f'%{search}%', per_page, offset))
+            else:
+                cursor.execute("SELECT id, name, simplified_name, sex FROM family_member_info LIMIT %s OFFSET %s", 
+                              (per_page, offset))
+            members = cursor.fetchall()
+            
+            return jsonify({"members": members, "total": total})
+    except Exception as e:
+        return jsonify({"success": False, "message": f"获取成员失败: {e}"}), 500
+    finally:
+        conn.close()
+
+@app.route('/manager/api/member/<int:member_id>')
+def get_member(member_id):
+    if 'user_id' not in session:
+        return jsonify({"success": False, "message": "Unauthorized"}), 401
+    
+    conn = get_db_connection()
+    try:
+        with conn.cursor() as cursor:
+            cursor.execute("SELECT id, name, name_word_generation FROM family_member_info WHERE id = %s", (member_id,))
+            member = cursor.fetchone()
+            if not member:
+                return jsonify({"success": False, "message": "成员不存在"}), 404
+            return jsonify({"member": member})
+    except Exception as e:
+        return jsonify({"success": False, "message": f"获取成员失败: {e}"}), 500
+    finally:
+        conn.close()
+
 @app.route('/manager/api/check_relations', methods=['POST'])
 @app.route('/manager/api/check_relations', methods=['POST'])
 def check_relations():
 def check_relations():
     if 'user_id' not in session:
     if 'user_id' not in session:
@@ -938,8 +992,9 @@ def add_member():
                                     "message": error_msg
                                     "message": error_msg
                                 }), 400
                                 }), 400
                             
                             
+                            selected_member_name = ''
                             return render_template('add_member.html', all_members=all_members, images=images, 
                             return render_template('add_member.html', all_members=all_members, images=images, 
-                                                 prefilled_content=prefilled_content, source_oss_url=source_oss_url, source_record_id=source_record_id)
+                                   prefilled_content=prefilled_content, source_oss_url=source_oss_url, source_record_id=source_record_id, selected_member_name=selected_member_name)
 
 
             # 获取表单数据
             # 获取表单数据
             data = {
             data = {
@@ -1049,8 +1104,9 @@ def add_member():
     finally:
     finally:
         conn.close()
         conn.close()
         
         
+    selected_member_name = ''
     return render_template('add_member.html', all_members=all_members, images=images, 
     return render_template('add_member.html', all_members=all_members, images=images, 
-                         prefilled_content=prefilled_content, source_oss_url=source_oss_url, source_record_id=source_record_id)
+           prefilled_content=prefilled_content, source_oss_url=source_oss_url, source_record_id=source_record_id, selected_member_name=selected_member_name)
 
 
 @app.route('/manager/edit_member/<int:member_id>', methods=['GET', 'POST'])
 @app.route('/manager/edit_member/<int:member_id>', methods=['GET', 'POST'])
 def edit_member(member_id):
 def edit_member(member_id):
@@ -1096,7 +1152,8 @@ def edit_member(member_id):
                                     "message": f"数据冲突:成员年龄不能比其父亲/母亲({parent['name']})大,请检查并修正出生日期。"
                                     "message": f"数据冲突:成员年龄不能比其父亲/母亲({parent['name']})大,请检查并修正出生日期。"
                                 }), 400
                                 }), 400
                             
                             
-                            return render_template('add_member.html', member=member, images=images, all_members=all_members)
+                            selected_member_name = ''
+                            return render_template('add_member.html', member=member, images=images, all_members=all_members, selected_member_name=selected_member_name)
 
 
             data = {
             data = {
                 'name': request.form['name'],
                 'name': request.form['name'],
@@ -1216,7 +1273,15 @@ def edit_member(member_id):
     finally:
     finally:
         conn.close()
         conn.close()
         
         
-    return render_template('add_member.html', member=member, images=images, all_members=all_members, current_relation=current_relation)
+    # Calculate selected_member_name based on current_relation
+    selected_member_name = ''
+    if current_relation and current_relation['parent_mid']:
+        for m in all_members:
+            if m['id'] == current_relation['parent_mid']:
+                selected_member_name = m['name']
+                break
+    
+    return render_template('add_member.html', member=member, images=images, all_members=all_members, current_relation=current_relation, selected_member_name=selected_member_name)
 
 
 @app.route('/manager/member_detail/<int:member_id>')
 @app.route('/manager/member_detail/<int:member_id>')
 def member_detail(member_id):
 def member_detail(member_id):

+ 246 - 8
templates/add_member.html

@@ -183,14 +183,13 @@
                     <div class="row g-3 mb-4">
                     <div class="row g-3 mb-4">
                         <div class="col-md-5">
                         <div class="col-md-5">
                             <label class="form-label">关联成员</label>
                             <label class="form-label">关联成员</label>
-                            <select name="related_mid" class="form-select">
-                                <option value="">-- 请选择 --</option>
-                                {% for m in all_members %}
-                                <option value="{{ m.id }}" data-birthday="{{ m.birthday }}" {{ 'selected' if current_relation and current_relation.parent_mid == m.id else '' }}>
-                                    {{ m.name }} (ID: {{ m.id }})
-                                </option>
-                                {% endfor %}
-                            </select>
+                            <div class="input-group">
+                                <input type="text" id="related-member-display" class="form-control" placeholder="点击选择关联成员" readonly value="{{ selected_member_name }}">
+                                <input type="hidden" name="related_mid" id="related_mid" value="{{ current_relation.parent_mid if current_relation else '' }}">
+                                <button type="button" class="btn btn-outline-primary" data-bs-toggle="modal" data-bs-target="#memberSelectModal">
+                                    <i class="bi bi-search"></i>
+                                </button>
+                            </div>
                         </div>
                         </div>
                         <div class="col-md-4">
                         <div class="col-md-4">
                             <label class="form-label">关系类型</label>
                             <label class="form-label">关系类型</label>
@@ -414,12 +413,60 @@
             <button onclick="nextImage()" class="btn btn-sm btn-outline-secondary">下一张</button>
             <button onclick="nextImage()" class="btn btn-sm btn-outline-secondary">下一张</button>
         </div>
         </div>
     </div>
     </div>
+
+    <!-- 成员选择弹窗 -->
+    <div class="modal fade" id="memberSelectModal" tabindex="-1" aria-hidden="true">
+        <div class="modal-dialog modal-lg">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h5 class="modal-title"><i class="bi bi-people me-2"></i>选择关联成员</h5>
+                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                </div>
+                <div class="modal-body">
+                    <div class="mb-4">
+                        <div class="input-group">
+                            <input type="text" id="member-search" class="form-control" placeholder="搜索成员姓名(支持繁体和简体)">
+                            <button type="button" class="btn btn-outline-primary" onclick="searchMembers()">
+                                <i class="bi bi-search"></i> 搜索
+                            </button>
+                        </div>
+                    </div>
+                    <div id="member-list" class="list-group mb-4 max-h-96 overflow-y-auto">
+                        <!-- 成员列表将通过JavaScript动态生成 -->
+                    </div>
+                    <div class="d-flex justify-content-between align-items-center">
+                        <div class="text-muted small">共 <span id="total-members">0</span> 个成员</div>
+                        <nav>
+                            <ul class="pagination pagination-sm">
+                                <li class="page-item disabled">
+                                    <a class="page-link" href="#" onclick="changePage(0)">上一页</a>
+                                </li>
+                                <li class="page-item active">
+                                    <a class="page-link" href="#" onclick="changePage(1)">1</a>
+                                </li>
+                                <li class="page-item">
+                                    <a class="page-link" href="#" onclick="changePage(2)">下一页</a>
+                                </li>
+                            </ul>
+                        </nav>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
+                </div>
+            </div>
+        </div>
+    </div>
 </div>
 </div>
 {% endblock %}
 {% endblock %}
 
 
 {% block extra_js %}
 {% block extra_js %}
 <script>
 <script>
     let tomSelectInstance = null;
     let tomSelectInstance = null;
+    let currentPage = 1;
+    let totalPages = 1;
+    let totalMembers = 0;
+    let membersData = [];
     function toggleBirthdayUnknown() {
     function toggleBirthdayUnknown() {
         const cb = document.getElementById('birthdayUnknown');
         const cb = document.getElementById('birthdayUnknown');
         const input = document.querySelector('input[name="birthday"]');
         const input = document.querySelector('input[name="birthday"]');
@@ -1946,5 +1993,196 @@
             addButton.classList.remove('d-none');
             addButton.classList.remove('d-none');
         });
         });
     });
     });
+
+    // 加载成员数据
+    function loadMembers(page = 1, search = '') {
+        fetch(`/manager/api/members?page=${page}&search=${encodeURIComponent(search)}`)
+            .then(response => response.json())
+            .then(data => {
+                membersData = data.members;
+                totalMembers = data.total;
+                totalPages = Math.ceil(totalMembers / 10);
+                currentPage = page;
+                
+                // 更新成员列表
+                updateMemberList();
+                // 更新分页
+                updatePagination();
+                // 更新总数
+                document.getElementById('total-members').textContent = totalMembers;
+            });
+    }
+
+    // 更新成员列表
+    function updateMemberList() {
+        const memberList = document.getElementById('member-list');
+        memberList.innerHTML = '';
+        
+        if (membersData.length === 0) {
+            memberList.innerHTML = '<div class="list-group-item text-center text-muted">暂无成员数据</div>';
+            return;
+        }
+        
+        membersData.forEach(member => {
+            const item = document.createElement('div');
+            item.className = 'list-group-item list-group-item-action';
+            item.onclick = () => selectMember(member);
+            item.innerHTML = `
+                <div class="d-flex justify-content-between align-items-center">
+                    <div>
+                        <h6 class="mb-0">${member.name}</h6>
+                        <small class="text-muted">ID: ${member.id} | ${member.sex === 1 ? '男' : '女'}</small>
+                    </div>
+                    <button type="button" class="btn btn-sm btn-outline-primary" onclick="event.stopPropagation(); selectMember(member);">
+                        选择
+                    </button>
+                </div>
+            `;
+            memberList.appendChild(item);
+        });
+    }
+
+    // 更新分页
+    function updatePagination() {
+        const pagination = document.querySelector('.pagination');
+        pagination.innerHTML = '';
+        
+        // 上一页
+        const prevLi = document.createElement('li');
+        prevLi.className = `page-item ${currentPage === 1 ? 'disabled' : ''}`;
+        prevLi.innerHTML = `<a class="page-link" href="#" onclick="changePage(${currentPage - 1})">&laquo;</a>`;
+        pagination.appendChild(prevLi);
+        
+        // 页码
+        for (let i = 1; i <= totalPages; i++) {
+            const li = document.createElement('li');
+            li.className = `page-item ${i === currentPage ? 'active' : ''}`;
+            li.innerHTML = `<a class="page-link" href="#" onclick="changePage(${i})"><onclick="changePage(${i})">${i}</a>`;
+            pagination.appendChild(li);
+        }
+        
+        // 下一页
+        const nextLi = document.createElement('li');
+        nextLi.className = `page-item ${currentPage === totalPages ? 'disabled' : ''}`;
+        nextLi.innerHTML = `<a class="page-link" href="#" onclick="changePage(${currentPage + 1})">&raquo;</a>`;
+        pagination.appendChild(nextLi);
+    }
+
+    // 改变页码
+    function changePage(page) {
+        if (page < 1 || page > totalPages) return;
+        const search = document.getElementById('member-search').value;
+        loadMembers(page, search);
+    }
+
+    // 搜索成员
+    function searchMembers() {
+        const search = document.getElementById('member-search').value;
+        loadMembers(1, search);
+    }
+
+    // 选择成员
+    function selectMember(member) {
+        document.getElementById('related-member-display').value = member.name;
+        document.getElementById('related_mid').value = member.id;
+        
+        // 检查是否是父子关系,如果是,显示父亲的世系世代
+        const relationType = document.querySelector('select[name="relation_type"]').value;
+        if (relationType == 1) { // 父子关系
+            fetch(`/manager/api/member/${member.id}`)
+                .then(response => response.json())
+                .then(data => {
+                    if (data.member && data.member.name_word_generation) {
+                        // 显示父亲的世系世代
+                        const lineageContainer = document.getElementById('lineage-generations-container');
+                        const fatherLineageDiv = document.getElementById('father-lineage');
+                        
+                        if (!fatherLineageDiv) {
+                            const newDiv = document.createElement('div');
+                            newDiv.id = 'father-lineage';
+                            newDiv.className = 'alert alert-info alert-sm mb-3 p-2';
+                            newDiv.innerHTML = `
+                                <div class="d-flex align-items-center">
+                                    <i class="bi bi-info-circle me-2"></i>
+                                    <div>
+                                        <strong>父亲世系世代参考:</strong>
+                                        <span>${data.member.name_word_generation}</span>
+                                    </div>
+                                </div>
+                            `;
+                            lineageContainer.insertBefore(newDiv, lineageContainer.firstChild);
+                        } else {
+                            fatherLineageDiv.innerHTML = `
+                                <div class="d-flex align-items-center">
+                                    <i class="bi bi-info-circle me-2"></i>
+                                    <div>
+                                        <strong>父亲世系世代参考:</strong>
+                                        <span>${data.member.name_word_generation}</span>
+                                    </div>
+                                </div>
+                            `;
+                        }
+                    }
+                });
+        }
+        
+        // 关闭模态框
+        const modal = bootstrap.Modal.getInstance(document.getElementById('memberSelectModal'));
+        modal.hide();
+    }
+
+    // 监听关系类型变化
+    document.addEventListener('DOMContentLoaded', function() {
+        // 监听关系类型变化
+        const relationTypeSelect = document.querySelector('select[name="relation_type"]');
+        if (relationTypeSelect) {
+            relationTypeSelect.addEventListener('change', function() {
+                const relatedMid = document.getElementById('related_mid').value;
+                if (this.value == 1 && relatedMid) {
+                    // 如果选择了父子关系且已选择关联成员,显示父亲的世系世代
+                    fetch(`/manager/api/member/${relatedMid}`)
+                        .then(response => response.json())
+                        .then(data => {
+                            if (data.member && data.member.name_word_generation) {
+                                const lineageContainer = document.getElementById('lineage-generations-container');
+                                const fatherLineageDiv = document.getElementById('father-lineage');
+                                
+                                if (!fatherLineageDiv) {
+                                    const newDiv = document.createElement('div');
+                                    newDiv.id = 'father-lineage';
+                                    newDiv.className = 'alert alert-info alert-sm mb-3 p-2';
+                                    newDiv.innerHTML = `
+                                        <div class="d-flex align-items-center">
+                                            <i class="bi bi-info-circle me-2"></i>
+                                            <div>
+                                                <strong>父亲世系世代参考:</strong>
+                                                <span>${data.member.name_word_generation}</span>
+                                            </div>
+                                        </div>
+                                    `;
+                                    lineageContainer.insertBefore(newDiv, lineageContainer.firstChild);
+                                } else {
+                                    fatherLineageDiv.innerHTML = `
+                                        <div class="d-flex align-items-center">
+                                            <i class="bi bi-info-circle me-2"></i>
+                                            <div>
+                                                <strong>父亲世系世代参考:</strong>
+                                                <span>${data.member.name_word_generation}</span>
+                                            </div>
+                                        </div>
+                                    `;
+                                }
+                            }
+                        });
+                }
+            });
+        }
+        
+        // 监听成员选择模态框显示
+        const memberSelectModal = document.getElementById('memberSelectModal');
+        memberSelectModal.addEventListener('shown.bs.modal', function() {
+            loadMembers();
+        });
+    });
 </script>
 </script>
 {% endblock %}
 {% endblock %}