|
@@ -0,0 +1,856 @@
|
|
|
|
|
+{% extends 'layout.html' %}
|
|
|
|
|
+
|
|
|
|
|
+{% block content %}
|
|
|
|
|
+<script>
|
|
|
|
|
+ window.isSuperAdmin = {{ session.get('is_super_admin') | tojson }};
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<script type="text/javascript">
|
|
|
|
|
+ window.AMapPromise = new Promise(function(resolve, reject) {
|
|
|
|
|
+ if (typeof AMap !== 'undefined') {
|
|
|
|
|
+ resolve(AMap);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const script = document.createElement('script');
|
|
|
|
|
+ script.src = 'https://webapi.amap.com/maps?v=2.0&key=7bbc2afa6f49b58024780bf647c6f038';
|
|
|
|
|
+ script.onload = function() { resolve(AMap); };
|
|
|
|
|
+ script.onerror = function() { reject('高德地图加载失败'); };
|
|
|
|
|
+ document.head.appendChild(script);
|
|
|
|
|
+ });
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<div class="container-fluid">
|
|
|
|
|
+ <div class="row">
|
|
|
|
|
+ <div class="col-md-12">
|
|
|
|
|
+ <div class="d-flex justify-content-between align-items-center mb-4">
|
|
|
|
|
+ <h2 class="page-header">聚落地图</h2>
|
|
|
|
|
+ <div class="d-flex gap-2">
|
|
|
|
|
+ <div class="btn-group" role="group">
|
|
|
|
|
+ <button type="button" class="btn btn-primary" id="view-map-btn">地图查看</button>
|
|
|
|
|
+ <button type="button" class="btn btn-outline-primary" id="view-list-btn">列表查看</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {% if session.get('is_super_admin') %}
|
|
|
|
|
+ <button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addSettlementModal">
|
|
|
|
|
+ <i class="bi bi-plus-circle"></i> 添加聚落
|
|
|
|
|
+ </button>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div id="map-view" class="mb-4" style="display: block;">
|
|
|
|
|
+ <div id="map-container" style="min-height: 500px; max-height: 80vh; border-radius: 12px; border: 1px solid #e2e8f0; overflow: hidden;">
|
|
|
|
|
+ <div class="map-loading" style="position: absolute; inset: 0; background: linear-gradient(135deg, #e0f2fe 0%, #f0f9ff 100%); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 100;">
|
|
|
|
|
+ <div class="spinner-border text-primary" role="status">
|
|
|
|
|
+ <span class="visually-hidden">加载中...</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p class="mt-3 text-gray-500">地图加载中...</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div id="list-view" class="mb-4" style="display: none;">
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <div class="table-responsive">
|
|
|
|
|
+ <table class="table table-hover">
|
|
|
|
|
+ <thead>
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th>编号</th>
|
|
|
|
|
+ <th>聚落名称</th>
|
|
|
|
|
+ <th>所属区域</th>
|
|
|
|
|
+ <th>人数</th>
|
|
|
|
|
+ <th>代表人物</th>
|
|
|
|
|
+ <th>姓氏类型</th>
|
|
|
|
|
+ <th>创建时间</th>
|
|
|
|
|
+ {% if session.get('is_super_admin') %}
|
|
|
|
|
+ <th>操作</th>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ </thead>
|
|
|
|
|
+ <tbody id="settlements-table-body">
|
|
|
|
|
+ </tbody>
|
|
|
|
|
+ </table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</div>
|
|
|
|
|
+
|
|
|
|
|
+<div class="modal fade" id="addSettlementModal" tabindex="-1" aria-labelledby="addSettlementModalLabel" aria-hidden="true">
|
|
|
|
|
+ <div class="modal-dialog modal-lg">
|
|
|
|
|
+ <div class="modal-content">
|
|
|
|
|
+ <div class="modal-header">
|
|
|
|
|
+ <h5 class="modal-title" id="addSettlementModalLabel">添加聚落</h5>
|
|
|
|
|
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-body">
|
|
|
|
|
+ <form id="settlement-form">
|
|
|
|
|
+ <input type="hidden" id="settlement-id">
|
|
|
|
|
+ <div class="row g-3">
|
|
|
|
|
+ <div class="col-md-6">
|
|
|
|
|
+ <label class="form-label">聚落名称 *</label>
|
|
|
|
|
+ <input type="text" class="form-control" id="settlement-name" required>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6">
|
|
|
|
|
+ <label class="form-label">所属区域 *</label>
|
|
|
|
|
+ <div class="input-group">
|
|
|
|
|
+ <input type="text" class="form-control" id="settlement-region" placeholder="搜索城市或区域名称" required>
|
|
|
|
|
+ <button type="button" class="btn btn-outline-primary" id="search-region-btn">
|
|
|
|
|
+ <i class="bi bi-search"></i>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div id="region-suggestions" class="mt-2" style="display: none;"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6 d-none">
|
|
|
|
|
+ <label class="form-label">纬度</label>
|
|
|
|
|
+ <input type="number" class="form-control" id="settlement-latitude" step="0.0000001">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6 d-none">
|
|
|
|
|
+ <label class="form-label">经度</label>
|
|
|
|
|
+ <input type="number" class="form-control" id="settlement-longitude" step="0.0000001">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-12">
|
|
|
|
|
+ <div class="alert alert-info alert-dismissible fade show" id="location-info" style="display: none;">
|
|
|
|
|
+ <i class="bi bi-info-circle me-2"></i>
|
|
|
|
|
+ <span id="location-info-text"></span>
|
|
|
|
|
+ <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6">
|
|
|
|
|
+ <label class="form-label">聚落人数</label>
|
|
|
|
|
+ <input type="number" class="form-control" id="settlement-population" min="0">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6">
|
|
|
|
|
+ <label class="form-label">代表人物</label>
|
|
|
|
|
+ <div class="input-group">
|
|
|
|
|
+ <input type="text" id="settlement-representative-display" class="form-control" placeholder="点击选择代表人物" readonly>
|
|
|
|
|
+ <input type="hidden" id="settlement-representative-id">
|
|
|
|
|
+ <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 class="col-md-6">
|
|
|
|
|
+ <label class="form-label">姓氏类型</label>
|
|
|
|
|
+ <select class="form-select" id="settlement-surname-type">
|
|
|
|
|
+ <option value="0">留姓</option>
|
|
|
|
|
+ <option value="1">改姓</option>
|
|
|
|
|
+ </select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6" id="new-surname-container" style="display: none;">
|
|
|
|
|
+ <label class="form-label">改后姓氏</label>
|
|
|
|
|
+ <input type="text" class="form-control" id="settlement-new-surname" placeholder="输入改后的姓氏">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-12">
|
|
|
|
|
+ <label class="form-label">备注说明</label>
|
|
|
|
|
+ <textarea class="form-control" id="settlement-description" rows="3"></textarea>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-footer">
|
|
|
|
|
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
|
|
|
+ <button type="button" class="btn btn-primary" id="save-settlement-btn">保存</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</div>
|
|
|
|
|
+
|
|
|
|
|
+<div class="modal fade" id="memberSelectModal" tabindex="-1" aria-labelledby="memberSelectModalLabel" aria-hidden="true">
|
|
|
|
|
+ <div class="modal-dialog modal-lg">
|
|
|
|
|
+ <div class="modal-content">
|
|
|
|
|
+ <div class="modal-header">
|
|
|
|
|
+ <h5 class="modal-title" id="memberSelectModalLabel">选择代表人物</h5>
|
|
|
|
|
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-body">
|
|
|
|
|
+ <div class="mb-3">
|
|
|
|
|
+ <input type="text" id="member-search-input" class="form-control" placeholder="搜索成员名称...">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div id="member-list-container" style="max-height: 400px; overflow-y: auto;">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-footer">
|
|
|
|
|
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</div>
|
|
|
|
|
+
|
|
|
|
|
+<div class="modal fade" id="settlementDetailModal" tabindex="-1" aria-labelledby="settlementDetailModalLabel" aria-hidden="true">
|
|
|
|
|
+ <div class="modal-dialog">
|
|
|
|
|
+ <div class="modal-content">
|
|
|
|
|
+ <div class="modal-header">
|
|
|
|
|
+ <h5 class="modal-title" id="settlementDetailModalLabel">聚落详情</h5>
|
|
|
|
|
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-body" id="settlement-detail-content">
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="modal-footer">
|
|
|
|
|
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
|
|
|
|
|
+ {% if session.get('is_super_admin') %}
|
|
|
|
|
+ <button type="button" class="btn btn-primary" id="edit-settlement-btn">编辑</button>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</div>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+let map = null;
|
|
|
|
|
+let markers = [];
|
|
|
|
|
+let settlementsData = [];
|
|
|
|
|
+
|
|
|
|
|
+function resizeMapContainer() {
|
|
|
|
|
+ const mapContainer = document.getElementById('map-container');
|
|
|
|
|
+ const headerHeight = document.querySelector('.page-header')?.offsetHeight || 80;
|
|
|
|
|
+ const toolbarHeight = 60;
|
|
|
|
|
+ const padding = 40;
|
|
|
|
|
+ const windowHeight = window.innerHeight;
|
|
|
|
|
+ const sidebarWidth = 250;
|
|
|
|
|
+ const availableHeight = windowHeight - headerHeight - toolbarHeight - padding;
|
|
|
|
|
+
|
|
|
|
|
+ if (window.innerWidth > 768) {
|
|
|
|
|
+ mapContainer.style.height = Math.max(500, Math.min(availableHeight, windowHeight * 0.85)) + 'px';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ mapContainer.style.height = Math.max(400, availableHeight * 0.8) + 'px';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (window.map) {
|
|
|
|
|
+ window.map.resize();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
|
+ loadSettlements();
|
|
|
|
|
+ initRegionSearch();
|
|
|
|
|
+
|
|
|
|
|
+ resizeMapContainer();
|
|
|
|
|
+ window.addEventListener('resize', resizeMapContainer);
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('addSettlementModal').addEventListener('show.bs.modal', function() {
|
|
|
|
|
+ if (!window.isEditingSettlement) {
|
|
|
|
|
+ resetSettlementForm();
|
|
|
|
|
+ }
|
|
|
|
|
+ window.isEditingSettlement = false;
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('view-map-btn').addEventListener('click', function() {
|
|
|
|
|
+ document.getElementById('map-view').style.display = 'block';
|
|
|
|
|
+ document.getElementById('list-view').style.display = 'none';
|
|
|
|
|
+ this.classList.add('btn-primary');
|
|
|
|
|
+ this.classList.remove('btn-outline-primary');
|
|
|
|
|
+ document.getElementById('view-list-btn').classList.add('btn-outline-primary');
|
|
|
|
|
+ document.getElementById('view-list-btn').classList.remove('btn-primary');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('view-list-btn').addEventListener('click', function() {
|
|
|
|
|
+ document.getElementById('map-view').style.display = 'none';
|
|
|
|
|
+ document.getElementById('list-view').style.display = 'block';
|
|
|
|
|
+ this.classList.add('btn-primary');
|
|
|
|
|
+ this.classList.remove('btn-outline-primary');
|
|
|
|
|
+ document.getElementById('view-map-btn').classList.add('btn-outline-primary');
|
|
|
|
|
+ document.getElementById('view-map-btn').classList.remove('btn-primary');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('member-search-input').addEventListener('input', function() {
|
|
|
|
|
+ searchMembers(this.value);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('save-settlement-btn').addEventListener('click', saveSettlement);
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('edit-settlement-btn').addEventListener('click', function() {
|
|
|
|
|
+ const id = document.getElementById('settlement-id').value;
|
|
|
|
|
+ if (id) {
|
|
|
|
|
+ loadSettlementForEdit(id);
|
|
|
|
|
+ }
|
|
|
|
|
+ const modal = bootstrap.Modal.getInstance(document.getElementById('settlementDetailModal'));
|
|
|
|
|
+ modal.hide();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.getElementById('settlement-surname-type').addEventListener('change', function() {
|
|
|
|
|
+ toggleNewSurnameField(this.value);
|
|
|
|
|
+ });
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+function loadSettlements() {
|
|
|
|
|
+ fetch('/manager/api/settlements', { credentials: 'include' })
|
|
|
|
|
+ .then(response => response.json())
|
|
|
|
|
+ .then(data => {
|
|
|
|
|
+ if (data.success) {
|
|
|
|
|
+ settlementsData = data.settlements;
|
|
|
|
|
+ renderSettlementList(data.settlements);
|
|
|
|
|
+ initMapWithData(data.settlements);
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(error => {
|
|
|
|
|
+ console.error('加载聚落失败:', error);
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function initMapWithData(settlements) {
|
|
|
|
|
+ window.AMapPromise.then(function(AMap) {
|
|
|
|
|
+ document.querySelector('.map-loading').style.display = 'none';
|
|
|
|
|
+
|
|
|
|
|
+ if (!map) {
|
|
|
|
|
+ let initialCenter = [116.397428, 39.90923];
|
|
|
|
|
+ let initialZoom = 4;
|
|
|
|
|
+
|
|
|
|
|
+ if (settlements.length > 0) {
|
|
|
|
|
+ const firstLng = parseFloat(settlements[0].longitude) || 116.397428;
|
|
|
|
|
+ const firstLat = parseFloat(settlements[0].latitude) || 39.90923;
|
|
|
|
|
+ initialCenter = [firstLng, firstLat];
|
|
|
|
|
+ initialZoom = settlements.length === 1 ? 10 : 6;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ map = new AMap.Map('map-container', {
|
|
|
|
|
+ center: initialCenter,
|
|
|
|
|
+ zoom: initialZoom,
|
|
|
|
|
+ resizeEnable: true,
|
|
|
|
|
+ mapStyle: 'amap://styles/normal',
|
|
|
|
|
+ features: ['bg', 'road', 'building', 'point', 'label'],
|
|
|
|
|
+ viewMode: '2D'
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ AMap.plugin(['AMap.Scale', 'AMap.ToolBar', 'AMap.LabelsLayer'], function() {
|
|
|
|
|
+ map.addControl(new AMap.Scale({ position: 'LB' }));
|
|
|
|
|
+ map.addControl(new AMap.ToolBar({ position: 'RB' }));
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ clearMarkers();
|
|
|
|
|
+
|
|
|
|
|
+ if (settlements.length > 0) {
|
|
|
|
|
+ const bounds = new AMap.Bounds();
|
|
|
|
|
+
|
|
|
|
|
+ settlements.forEach(settlement => {
|
|
|
|
|
+ const lng = parseFloat(settlement.longitude) || 116.397428;
|
|
|
|
|
+ const lat = parseFloat(settlement.latitude) || 39.90923;
|
|
|
|
|
+
|
|
|
|
|
+ const circle = new AMap.Circle({
|
|
|
|
|
+ center: [lng, lat],
|
|
|
|
|
+ radius: 50000,
|
|
|
|
|
+ strokeColor: '#3B82F6',
|
|
|
|
|
+ strokeOpacity: 0.8,
|
|
|
|
|
+ strokeWeight: 3,
|
|
|
|
|
+ fillColor: '#3B82F6',
|
|
|
|
|
+ fillOpacity: 0.15,
|
|
|
|
|
+ cursor: 'pointer'
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ circle.settlementData = settlement;
|
|
|
|
|
+ circle.on('click', function() { showSettlementDetail(settlement); });
|
|
|
|
|
+ circle.on('mouseover', function(e) {
|
|
|
|
|
+ circle.setOptions({
|
|
|
|
|
+ fillOpacity: 0.25,
|
|
|
|
|
+ strokeOpacity: 1
|
|
|
|
|
+ });
|
|
|
|
|
+ showMarkerTooltip(e, settlement);
|
|
|
|
|
+ });
|
|
|
|
|
+ circle.on('mouseout', function() {
|
|
|
|
|
+ circle.setOptions({
|
|
|
|
|
+ fillOpacity: 0.15,
|
|
|
|
|
+ strokeOpacity: 0.8
|
|
|
|
|
+ });
|
|
|
|
|
+ hideMarkerTooltip();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ map.add(circle);
|
|
|
|
|
+ markers.push(circle);
|
|
|
|
|
+ bounds.extend([lng, lat]);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (settlements.length === 1) {
|
|
|
|
|
+ const lng = parseFloat(settlements[0].longitude) || 116.397428;
|
|
|
|
|
+ const lat = parseFloat(settlements[0].latitude) || 39.90923;
|
|
|
|
|
+ map.setCenter([lng, lat]);
|
|
|
|
|
+ map.setZoom(10);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ setTimeout(function() {
|
|
|
|
|
+ map.setFitView(bounds, false, [50, 50, 50, 50]);
|
|
|
|
|
+ }, 100);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(function(err) {
|
|
|
|
|
+ console.error('高德地图加载失败:', err);
|
|
|
|
|
+ document.querySelector('.map-loading p').textContent = '地图加载失败';
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function clearMarkers() {
|
|
|
|
|
+ markers.forEach(marker => { map.remove(marker); });
|
|
|
|
|
+ markers = [];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function renderSettlementList(settlements) {
|
|
|
|
|
+ const tbody = document.getElementById('settlements-table-body');
|
|
|
|
|
+ tbody.innerHTML = '';
|
|
|
|
|
+
|
|
|
|
|
+ if (settlements.length === 0) {
|
|
|
|
|
+ tbody.innerHTML = '<tr><td colspan="8" class="text-center text-gray-500">暂无聚落数据</td></tr>';
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ settlements.forEach((settlement, index) => {
|
|
|
|
|
+ const surnameType = getSurnameTypeText(settlement.surname_type);
|
|
|
|
|
+ const newSurname = settlement.new_surname ? '(' + settlement.new_surname + ')' : '';
|
|
|
|
|
+ const row = document.createElement('tr');
|
|
|
|
|
+ row.innerHTML = `
|
|
|
|
|
+ <td>${index + 1}</td>
|
|
|
|
|
+ <td>${settlement.name}</td>
|
|
|
|
|
+ <td>${settlement.region || '-'}</td>
|
|
|
|
|
+ <td>${settlement.population || 0}</td>
|
|
|
|
|
+ <td>${settlement.representative_name || '-'}</td>
|
|
|
|
|
+ <td>${surnameType}${newSurname}</td>
|
|
|
|
|
+ <td>${formatDateTime(settlement.created_at)}</td>
|
|
|
|
|
+ ${window.isSuperAdmin ? `
|
|
|
|
|
+ <td>
|
|
|
|
|
+ <button class="btn btn-sm btn-primary edit-settlement-btn" data-id="${settlement.id}">编辑</button>
|
|
|
|
|
+ <button class="btn btn-sm btn-danger delete-settlement-btn" data-id="${settlement.id}">删除</button>
|
|
|
|
|
+ </td>
|
|
|
|
|
+ ` : ''}
|
|
|
|
|
+ `;
|
|
|
|
|
+
|
|
|
|
|
+ row.addEventListener('click', function() { showSettlementDetail(settlement); });
|
|
|
|
|
+ tbody.appendChild(row);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.querySelectorAll('.edit-settlement-btn').forEach(btn => {
|
|
|
|
|
+ btn.addEventListener('click', function(e) {
|
|
|
|
|
+ e.stopPropagation();
|
|
|
|
|
+ loadSettlementForEdit(this.dataset.id);
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.querySelectorAll('.delete-settlement-btn').forEach(btn => {
|
|
|
|
|
+ btn.addEventListener('click', function(e) {
|
|
|
|
|
+ e.stopPropagation();
|
|
|
|
|
+ if (confirm('确定要删除这个聚落吗?')) {
|
|
|
|
|
+ deleteSettlement(this.dataset.id);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function showSettlementDetail(settlement) {
|
|
|
|
|
+ document.getElementById('settlement-id').value = settlement.id;
|
|
|
|
|
+ const surnameType = getSurnameTypeText(settlement.surname_type);
|
|
|
|
|
+ const content = document.getElementById('settlement-detail-content');
|
|
|
|
|
+ content.innerHTML = `
|
|
|
|
|
+ <div class="mb-4">
|
|
|
|
|
+ <h4>${settlement.name}</h4>
|
|
|
|
|
+ ${settlement.region ? '<p class="text-muted">区域:' + settlement.region + '</p>' : ''}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="row">
|
|
|
|
|
+ <div class="col-md-6">
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <h6 class="card-title">基本信息</h6>
|
|
|
|
|
+ <p class="card-text"><strong>人数:</strong>${settlement.population || 0} 人</p>
|
|
|
|
|
+ ${settlement.latitude ? '<p class="card-text"><strong>纬度:</strong>' + settlement.latitude + '</p>' : ''}
|
|
|
|
|
+ ${settlement.longitude ? '<p class="card-text"><strong>经度:</strong>' + settlement.longitude + '</p>' : ''}
|
|
|
|
|
+ <p class="card-text"><strong>姓氏类型:</strong>${surnameType}${settlement.new_surname ? '(改后:' + settlement.new_surname + ')' : ''}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="col-md-6">
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <h6 class="card-title">代表人物</h6>
|
|
|
|
|
+ <p class="card-text">${settlement.representative_name || '未设置'}</p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ ${settlement.description ? '<div class="mt-4"><h6>备注说明</h6><p>' + settlement.description + '</p></div>' : ''}
|
|
|
|
|
+ <div class="mt-4 text-muted text-sm">
|
|
|
|
|
+ 创建时间:${formatDateTime(settlement.created_at)}
|
|
|
|
|
+ ${settlement.updated_at !== settlement.created_at ? '<br>更新时间:' + formatDateTime(settlement.updated_at) : ''}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ `;
|
|
|
|
|
+
|
|
|
|
|
+ const modal = new bootstrap.Modal(document.getElementById('settlementDetailModal'));
|
|
|
|
|
+ modal.show();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function showSettlementDetailById(id) {
|
|
|
|
|
+ fetch('/manager/api/settlements/' + id)
|
|
|
|
|
+ .then(response => response.json())
|
|
|
|
|
+ .then(data => {
|
|
|
|
|
+ if (data.success) {
|
|
|
|
|
+ showSettlementDetail(data.settlement);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function loadSettlementForEdit(id) {
|
|
|
|
|
+ fetch('/manager/api/settlements/' + id)
|
|
|
|
|
+ .then(response => response.json())
|
|
|
|
|
+ .then(data => {
|
|
|
|
|
+ if (data.success) {
|
|
|
|
|
+ const s = data.settlement;
|
|
|
|
|
+ document.getElementById('settlement-id').value = s.id;
|
|
|
|
|
+ document.getElementById('settlement-name').value = s.name;
|
|
|
|
|
+ document.getElementById('settlement-region').value = s.region || '';
|
|
|
|
|
+ document.getElementById('settlement-latitude').value = s.latitude || '';
|
|
|
|
|
+ document.getElementById('settlement-longitude').value = s.longitude || '';
|
|
|
|
|
+ document.getElementById('settlement-population').value = s.population || '';
|
|
|
|
|
+ document.getElementById('settlement-representative-id').value = s.representative_id || '';
|
|
|
|
|
+ document.getElementById('settlement-representative-display').value = s.representative_name || '';
|
|
|
|
|
+ document.getElementById('settlement-description').value = s.description || '';
|
|
|
|
|
+ document.getElementById('settlement-surname-type').value = s.surname_type || 0;
|
|
|
|
|
+ document.getElementById('settlement-new-surname').value = s.new_surname || '';
|
|
|
|
|
+
|
|
|
|
|
+ toggleNewSurnameField(s.surname_type);
|
|
|
|
|
+ document.getElementById('addSettlementModalLabel').textContent = '编辑聚落';
|
|
|
|
|
+
|
|
|
|
|
+ if (s.region && s.latitude) {
|
|
|
|
|
+ document.getElementById('location-info-text').innerHTML = '已选择区域:' + s.region + ',坐标:' + s.latitude + ', ' + s.longitude;
|
|
|
|
|
+ document.getElementById('location-info').style.display = 'block';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ window.isEditingSettlement = true;
|
|
|
|
|
+ const modal = new bootstrap.Modal(document.getElementById('addSettlementModal'));
|
|
|
|
|
+ modal.show();
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function resetSettlementForm() {
|
|
|
|
|
+ document.getElementById('settlement-id').value = '';
|
|
|
|
|
+ document.getElementById('settlement-name').value = '';
|
|
|
|
|
+ document.getElementById('settlement-region').value = '';
|
|
|
|
|
+ document.getElementById('settlement-latitude').value = '';
|
|
|
|
|
+ document.getElementById('settlement-longitude').value = '';
|
|
|
|
|
+ document.getElementById('settlement-population').value = '';
|
|
|
|
|
+ document.getElementById('settlement-representative-id').value = '';
|
|
|
|
|
+ document.getElementById('settlement-representative-display').value = '';
|
|
|
|
|
+ document.getElementById('settlement-description').value = '';
|
|
|
|
|
+ document.getElementById('settlement-surname-type').value = '0';
|
|
|
|
|
+ document.getElementById('settlement-new-surname').value = '';
|
|
|
|
|
+ document.getElementById('location-info').style.display = 'none';
|
|
|
|
|
+ document.getElementById('region-suggestions').style.display = 'none';
|
|
|
|
|
+ document.getElementById('new-surname-container').style.display = 'none';
|
|
|
|
|
+ document.getElementById('addSettlementModalLabel').textContent = '添加聚落';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function saveSettlement() {
|
|
|
|
|
+ const data = {
|
|
|
|
|
+ id: document.getElementById('settlement-id').value,
|
|
|
|
|
+ name: document.getElementById('settlement-name').value,
|
|
|
|
|
+ region: document.getElementById('settlement-region').value,
|
|
|
|
|
+ latitude: document.getElementById('settlement-latitude').value,
|
|
|
|
|
+ longitude: document.getElementById('settlement-longitude').value,
|
|
|
|
|
+ population: document.getElementById('settlement-population').value,
|
|
|
|
|
+ representative_id: document.getElementById('settlement-representative-id').value,
|
|
|
|
|
+ description: document.getElementById('settlement-description').value,
|
|
|
|
|
+ surname_type: document.getElementById('settlement-surname-type').value,
|
|
|
|
|
+ new_surname: document.getElementById('settlement-new-surname').value
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const url = data.id ? '/manager/api/settlements/' + data.id : '/manager/api/settlements';
|
|
|
|
|
+ const method = data.id ? 'PUT' : 'POST';
|
|
|
|
|
+
|
|
|
|
|
+ fetch(url, {
|
|
|
|
|
+ method: method,
|
|
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
+ body: JSON.stringify(data),
|
|
|
|
|
+ credentials: 'include'
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(response => response.json())
|
|
|
|
|
+ .then(data => {
|
|
|
|
|
+ if (data.success) {
|
|
|
|
|
+ alert('保存成功!');
|
|
|
|
|
+ const modal = bootstrap.Modal.getInstance(document.getElementById('addSettlementModal'));
|
|
|
|
|
+ modal.hide();
|
|
|
|
|
+ loadSettlements();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ alert(data.message || '保存失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(error => {
|
|
|
|
|
+ console.error('保存失败:', error);
|
|
|
|
|
+ alert('保存失败,请检查网络连接');
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function deleteSettlement(id) {
|
|
|
|
|
+ fetch('/manager/api/settlements/' + id, {
|
|
|
|
|
+ method: 'DELETE',
|
|
|
|
|
+ credentials: 'include'
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(response => response.json())
|
|
|
|
|
+ .then(data => {
|
|
|
|
|
+ if (data.success) {
|
|
|
|
|
+ loadSettlements();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ alert(data.message || '删除失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function searchMembers(keyword) {
|
|
|
|
|
+ fetch('/manager/api/search_member', {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: { 'Content-Type': 'application/json' },
|
|
|
|
|
+ body: JSON.stringify({ keyword: keyword })
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(response => response.json())
|
|
|
|
|
+ .then(data => {
|
|
|
|
|
+ const container = document.getElementById('member-list-container');
|
|
|
|
|
+ if (data.success && data.members.length > 0) {
|
|
|
|
|
+ container.innerHTML = data.members.map(member => {
|
|
|
|
|
+ const simplified = member.simplified_name && member.simplified_name !== member.name ? '(' + member.simplified_name + ')' : '';
|
|
|
|
|
+ return '<div class="member-item p-3 border-bottom cursor-pointer hover:bg-gray-50" onclick="selectRepresentative(' + member.id + ', \'' + member.name + '\', \'' + (member.simplified_name || '') + '\')">' +
|
|
|
|
|
+ '<div style="font-weight: 500;">' + member.name + '</div>' +
|
|
|
|
|
+ (member.simplified_name ? '<div style="font-size: 12px; color: #64748b;">' + simplified + '</div>' : '') +
|
|
|
|
|
+ '</div>';
|
|
|
|
|
+ }).join('');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ container.innerHTML = '<div class="text-center py-4 text-gray-500">未找到匹配的成员</div>';
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function selectRepresentative(id, name, simplifiedName) {
|
|
|
|
|
+ document.getElementById('settlement-representative-id').value = id;
|
|
|
|
|
+ let displayText = name;
|
|
|
|
|
+ if (simplifiedName && simplifiedName !== name) {
|
|
|
|
|
+ displayText += ' (' + simplifiedName + ')';
|
|
|
|
|
+ }
|
|
|
|
|
+ document.getElementById('settlement-representative-display').value = displayText;
|
|
|
|
|
+ const modal = bootstrap.Modal.getInstance(document.getElementById('memberSelectModal'));
|
|
|
|
|
+ modal.hide();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function formatDateTime(dateStr) {
|
|
|
|
|
+ if (!dateStr) return '-';
|
|
|
|
|
+ const date = new Date(dateStr);
|
|
|
|
|
+ return date.toLocaleString('zh-CN');
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function initRegionSearch() {
|
|
|
|
|
+ const regionInput = document.getElementById('settlement-region');
|
|
|
|
|
+ const suggestionsDiv = document.getElementById('region-suggestions');
|
|
|
|
|
+ const searchBtn = document.getElementById('search-region-btn');
|
|
|
|
|
+
|
|
|
|
|
+ searchBtn.addEventListener('click', function() {
|
|
|
|
|
+ const keyword = regionInput.value.trim();
|
|
|
|
|
+ if (keyword) {
|
|
|
|
|
+ searchRegion(keyword);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ let searchTimeout = null;
|
|
|
|
|
+ regionInput.addEventListener('input', function() {
|
|
|
|
|
+ const keyword = this.value.trim();
|
|
|
|
|
+ if (searchTimeout) clearTimeout(searchTimeout);
|
|
|
|
|
+ if (keyword.length >= 2) {
|
|
|
|
|
+ searchTimeout = setTimeout(() => { searchRegion(keyword); }, 300);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ suggestionsDiv.style.display = 'none';
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ regionInput.addEventListener('keypress', function(e) {
|
|
|
|
|
+ if (e.key === 'Enter') {
|
|
|
|
|
+ e.preventDefault();
|
|
|
|
|
+ const keyword = this.value.trim();
|
|
|
|
|
+ if (keyword) {
|
|
|
|
|
+ searchRegion(keyword);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ document.addEventListener('click', function(e) {
|
|
|
|
|
+ if (!e.target.closest('#settlement-region') && !e.target.closest('#search-region-btn') && !e.target.closest('#region-suggestions')) {
|
|
|
|
|
+ suggestionsDiv.style.display = 'none';
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function searchRegion(keyword) {
|
|
|
|
|
+ const suggestionsDiv = document.getElementById('region-suggestions');
|
|
|
|
|
+
|
|
|
|
|
+ window.AMapPromise.then(function(AMap) {
|
|
|
|
|
+ AMap.plugin(['AMap.PlaceSearch'], function() {
|
|
|
|
|
+ const placeSearch = new AMap.PlaceSearch({
|
|
|
|
|
+ city: '全国',
|
|
|
|
|
+ type: 'district',
|
|
|
|
|
+ pageSize: 10
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ placeSearch.search(keyword, function(status, result) {
|
|
|
|
|
+ if (status === 'complete' && result && result.poiList && result.poiList.pois.length > 0) {
|
|
|
|
|
+ const pois = result.poiList.pois;
|
|
|
|
|
+ const formattedData = pois.map(poi => ({
|
|
|
|
|
+ name: poi.name,
|
|
|
|
|
+ adcode: poi.adcode,
|
|
|
|
|
+ address: poi.address || poi.name,
|
|
|
|
|
+ location: {
|
|
|
|
|
+ lng: poi.location.lng || poi.location[0],
|
|
|
|
|
+ lat: poi.location.lat || poi.location[1]
|
|
|
|
|
+ }
|
|
|
|
|
+ }));
|
|
|
|
|
+ renderRegionSuggestions(formattedData);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ searchRegionFallback(keyword);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ }).catch(function() {
|
|
|
|
|
+ searchRegionFallback(keyword);
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function searchRegionFallback(keyword) {
|
|
|
|
|
+ const suggestionsDiv = document.getElementById('region-suggestions');
|
|
|
|
|
+
|
|
|
|
|
+ const localData = [
|
|
|
|
|
+ { name: '泉州市鲤城区', adcode: '350502', address: '福建省泉州市鲤城区', location: { lng: 118.58809, lat: 24.90757 } },
|
|
|
|
|
+ { name: '泉州市丰泽区', adcode: '350503', address: '福建省泉州市丰泽区', location: { lng: 118.62062, lat: 24.88269 } },
|
|
|
|
|
+ { name: '泉州市洛江区', adcode: '350504', address: '福建省泉州市洛江区', location: { lng: 118.74387, lat: 24.92516 } },
|
|
|
|
|
+ { name: '泉州市泉港区', adcode: '350505', address: '福建省泉州市泉港区', location: { lng: 118.96936, lat: 25.13699 } },
|
|
|
|
|
+ { name: '泉州市永春县', adcode: '350525', address: '福建省泉州市永春县', location: { lng: 118.38267, lat: 25.34437 } },
|
|
|
|
|
+ { name: '泉州市安溪县', adcode: '350524', address: '福建省泉州市安溪县', location: { lng: 118.18598, lat: 25.06531 } },
|
|
|
|
|
+ { name: '泉州市德化县', adcode: '350526', address: '福建省泉州市德化县', location: { lng: 118.15722, lat: 25.53543 } },
|
|
|
|
|
+ { name: '泉州市金门县', adcode: '350527', address: '福建省泉州市金门县', location: { lng: 118.32556, lat: 24.43788 } },
|
|
|
|
|
+ { name: '泉州市南安市', adcode: '350583', address: '福建省泉州市南安市', location: { lng: 118.32211, lat: 24.96694 } },
|
|
|
|
|
+ { name: '泉州市晋江市', adcode: '350582', address: '福建省泉州市晋江市', location: { lng: 118.56388, lat: 24.80951 } },
|
|
|
|
|
+ { name: '泉州市石狮市', adcode: '350581', address: '福建省泉州市石狮市', location: { lng: 118.65021, lat: 24.74517 } },
|
|
|
|
|
+ { name: '漳州市芗城区', adcode: '350602', address: '福建省漳州市芗城区', location: { lng: 117.62588, lat: 24.61406 } },
|
|
|
|
|
+ { name: '漳州市龙文区', adcode: '350603', address: '福建省漳州市龙文区', location: { lng: 117.71692, lat: 24.62148 } },
|
|
|
|
|
+ { name: '漳州市龙海市', adcode: '350681', address: '福建省漳州市龙海市', location: { lng: 117.80713, lat: 24.47173 } },
|
|
|
|
|
+ { name: '漳州市华安县', adcode: '350629', address: '福建省漳州市华安县', location: { lng: 117.4898, lat: 24.7568 } },
|
|
|
|
|
+ { name: '厦门市思明区', adcode: '350203', address: '福建省厦门市思明区', location: { lng: 118.08942, lat: 24.47977 } },
|
|
|
|
|
+ { name: '厦门市湖里区', adcode: '350206', address: '福建省厦门市湖里区', location: { lng: 118.14991, lat: 24.52864 } },
|
|
|
|
|
+ { name: '厦门市同安区', adcode: '350212', address: '福建省厦门市同安区', location: { lng: 118.16728, lat: 24.72657 } },
|
|
|
|
|
+ { name: '厦门市翔安区', adcode: '350213', address: '福建省厦门市翔安区', location: { lng: 118.26543, lat: 24.63788 } },
|
|
|
|
|
+ { name: '厦门市集美区', adcode: '350211', address: '福建省厦门市集美区', location: { lng: 118.09767, lat: 24.60224 } },
|
|
|
|
|
+ { name: '厦门市海沧区', adcode: '350205', address: '福建省厦门市海沧区', location: { lng: 117.97257, lat: 24.53645 } },
|
|
|
|
|
+ { name: '龙岩市漳平市', adcode: '350881', address: '福建省龙岩市漳平市', location: { lng: 117.45528, lat: 25.36948 } },
|
|
|
|
|
+ { name: '三明市大田县', adcode: '350425', address: '福建省三明市大田县', location: { lng: 117.83028, lat: 25.69053 } },
|
|
|
|
|
+ { name: '福州市鼓楼区', adcode: '350102', address: '福建省福州市鼓楼区', location: { lng: 119.29648, lat: 26.08744 } },
|
|
|
|
|
+ { name: '福州市台江区', adcode: '350103', address: '福建省福州市台江区', location: { lng: 119.30476, lat: 26.05936 } },
|
|
|
|
|
+ { name: '福州市仓山区', adcode: '350104', address: '福建省福州市仓山区', location: { lng: 119.27744, lat: 26.00716 } },
|
|
|
|
|
+ { name: '信阳市固始县', adcode: '411525', address: '河南省信阳市固始县', location: { lng: 115.68042, lat: 32.14447 } },
|
|
|
|
|
+ { name: '六安市寿县', adcode: '341521', address: '安徽省六安市寿县', location: { lng: 116.68658, lat: 32.57654 } },
|
|
|
|
|
+ { name: '六安市霍邱县', adcode: '341522', address: '安徽省六安市霍邱县', location: { lng: 116.10428, lat: 32.38912 } },
|
|
|
|
|
+ { name: '六安市李集', adcode: '341522', address: '安徽省六安市霍邱县李集镇', location: { lng: 115.92136, lat: 32.38456 } },
|
|
|
|
|
+ { name: '长沙市望城区', adcode: '430112', address: '湖南省长沙市望城区', location: { lng: 112.83488, lat: 28.31353 } },
|
|
|
|
|
+ { name: '广州市增城区', adcode: '440118', address: '广东省广州市增城区', location: { lng: 113.86262, lat: 23.23745 } },
|
|
|
|
|
+ { name: '吉安市永新县', adcode: '360830', address: '江西省吉安市永新县', location: { lng: 114.22658, lat: 26.90836 } },
|
|
|
|
|
+ { name: '丽水市青田县', adcode: '331121', address: '浙江省丽水市青田县', location: { lng: 120.28158, lat: 28.19935 } },
|
|
|
|
|
+ { name: '温州市泰顺县', adcode: '330329', address: '浙江省温州市泰顺县', location: { lng: 119.75248, lat: 27.67386 } },
|
|
|
|
|
+ { name: '温州市文成县', adcode: '330328', address: '浙江省温州市文成县', location: { lng: 120.08258, lat: 27.68862 } },
|
|
|
|
|
+ { name: '杭州市临安区', adcode: '330112', address: '浙江省杭州市临安区', location: { lng: 119.72768, lat: 30.23397 } },
|
|
|
|
|
+ { name: '金华市兰溪市', adcode: '330781', address: '浙江省金华市兰溪市', location: { lng: 119.48938, lat: 29.29735 } },
|
|
|
|
|
+ { name: '衢州市衢江区', adcode: '330803', address: '浙江省衢州市衢江区', location: { lng: 118.88088, lat: 28.93562 } },
|
|
|
|
|
+ { name: '衢州市开化县', adcode: '330824', address: '浙江省衢州市开化县', location: { lng: 118.30888, lat: 29.29412 } },
|
|
|
|
|
+ { name: '长春市农安县', adcode: '220122', address: '吉林省长春市农安县', location: { lng: 125.16718, lat: 44.45183 } },
|
|
|
|
|
+ { name: '新北市三重区', adcode: '710101', address: '台湾省新北市三重区', location: { lng: 121.48888, lat: 25.05722 } },
|
|
|
|
|
+ { name: '台北市', adcode: '710100', address: '台湾省台北市', location: { lng: 121.50906, lat: 25.04433 } },
|
|
|
|
|
+ { name: '云林县', adcode: '710600', address: '台湾省云林县', location: { lng: 120.43758, lat: 23.71465 } },
|
|
|
|
|
+ { name: '彰化县', adcode: '710500', address: '台湾省彰化县', location: { lng: 120.54869, lat: 24.08279 } },
|
|
|
|
|
+ { name: '台中市清水区', adcode: '710206', address: '台湾省台中市清水区', location: { lng: 120.56496, lat: 24.26271 } },
|
|
|
|
|
+ { name: '菲律宾马尼拉', adcode: 'PH001', address: '菲律宾马尼拉', location: { lng: 120.9842, lat: 14.5995 } },
|
|
|
|
|
+ { name: '北京市', adcode: '110000', address: '北京市', location: { lng: 116.407394, lat: 39.904211 } },
|
|
|
|
|
+ { name: '上海市', adcode: '310000', address: '上海市', location: { lng: 121.473701, lat: 31.230416 } },
|
|
|
|
|
+ { name: '广州市', adcode: '440100', address: '广东省广州市', location: { lng: 113.264385, lat: 23.12911 } },
|
|
|
|
|
+ { name: '深圳市', adcode: '440300', address: '广东省深圳市', location: { lng: 114.057964, lat: 22.543099 } },
|
|
|
|
|
+ { name: '杭州市', adcode: '330100', address: '浙江省杭州市', location: { lng: 120.197855, lat: 30.274084 } },
|
|
|
|
|
+ { name: '南京市', adcode: '320100', address: '江苏省南京市', location: { lng: 118.796875, lat: 32.060255 } },
|
|
|
|
|
+ { name: '成都市', adcode: '510100', address: '四川省成都市', location: { lng: 104.066801, lat: 30.572816 } },
|
|
|
|
|
+ { name: '武汉市', adcode: '420100', address: '湖北省武汉市', location: { lng: 114.287924, lat: 30.592855 } }
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ const filteredData = localData.filter(item => item.name.includes(keyword) || item.address.includes(keyword));
|
|
|
|
|
+
|
|
|
|
|
+ if (filteredData.length > 0) {
|
|
|
|
|
+ renderRegionSuggestions(filteredData);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ suggestionsDiv.innerHTML = '<div class="alert alert-warning">未找到匹配的区域,请尝试其他关键词</div>';
|
|
|
|
|
+ suggestionsDiv.style.display = 'block';
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function renderRegionSuggestions(pois) {
|
|
|
|
|
+ const suggestionsDiv = document.getElementById('region-suggestions');
|
|
|
|
|
+ let html = '<div class="list-group" style="max-height: 300px; overflow-y: auto;">';
|
|
|
|
|
+
|
|
|
|
|
+ pois.slice(0, 8).forEach(poi => {
|
|
|
|
|
+ const lng = poi.location.lng || poi.location[0];
|
|
|
|
|
+ const lat = poi.location.lat || poi.location[1];
|
|
|
|
|
+ html += '<button type="button" class="list-group-item list-group-item-action" onclick="selectRegion(\'' + poi.name + '\', ' + lng + ', ' + lat + ')">' +
|
|
|
|
|
+ '<div class="d-flex justify-content-between">' +
|
|
|
|
|
+ '<span style="font-weight: 500;">' + poi.name + '</span>' +
|
|
|
|
|
+ '<span class="text-muted text-sm">' + poi.adcode + '</span>' +
|
|
|
|
|
+ '</div>' +
|
|
|
|
|
+ (poi.address ? '<div class="text-sm text-muted">' + poi.address + '</div>' : '') +
|
|
|
|
|
+ '</button>';
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ html += '</div>';
|
|
|
|
|
+ suggestionsDiv.innerHTML = html;
|
|
|
|
|
+ suggestionsDiv.style.display = 'block';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function toggleNewSurnameField(surnameType) {
|
|
|
|
|
+ const container = document.getElementById('new-surname-container');
|
|
|
|
|
+ if (surnameType == 1) {
|
|
|
|
|
+ container.style.display = 'block';
|
|
|
|
|
+ } else {
|
|
|
|
|
+ container.style.display = 'none';
|
|
|
|
|
+ document.getElementById('settlement-new-surname').value = '';
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function getSurnameTypeText(type) {
|
|
|
|
|
+ return type == 1 ? '改姓' : '留姓';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function selectRegion(name, lng, lat) {
|
|
|
|
|
+ document.getElementById('settlement-region').value = name;
|
|
|
|
|
+ document.getElementById('settlement-latitude').value = lat;
|
|
|
|
|
+ document.getElementById('settlement-longitude').value = lng;
|
|
|
|
|
+ document.getElementById('region-suggestions').style.display = 'none';
|
|
|
|
|
+
|
|
|
|
|
+ const infoText = document.getElementById('location-info-text');
|
|
|
|
|
+ infoText.innerHTML = '已选择区域:' + name + ',坐标:' + lat.toFixed(6) + ', ' + lng.toFixed(6);
|
|
|
|
|
+ document.getElementById('location-info').style.display = 'block';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+let tooltipDiv = null;
|
|
|
|
|
+
|
|
|
|
|
+function showMarkerTooltip(e, settlement) {
|
|
|
|
|
+ if (!tooltipDiv) {
|
|
|
|
|
+ tooltipDiv = document.createElement('div');
|
|
|
|
|
+ tooltipDiv.style.cssText = 'position: fixed; background: rgba(30, 41, 59, 0.95); color: white; padding: 12px; border-radius: 8px; min-width: 200px; box-shadow: 0 4px 20px rgba(0,0,0,0.3); z-index: 1000; pointer-events: none; font-size: 13px;';
|
|
|
|
|
+ document.body.appendChild(tooltipDiv);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const surnameType = getSurnameTypeText(settlement.surname_type);
|
|
|
|
|
+ tooltipDiv.innerHTML = '<div style="font-weight: 600; margin-bottom: 8px; font-size: 14px;">' + settlement.name + '</div>' +
|
|
|
|
|
+ (settlement.region ? '<div style="margin-bottom: 4px; color: #94a3b8;">区域:' + settlement.region + '</div>' : '') +
|
|
|
|
|
+ '<div style="margin-bottom: 4px; color: #94a3b8;">人数:' + (settlement.population || 0) + ' 人</div>' +
|
|
|
|
|
+ (settlement.representative_name ? '<div style="margin-bottom: 4px; color: #94a3b8;">代表:' + settlement.representative_name + '</div>' : '') +
|
|
|
|
|
+ '<div style="margin-bottom: 4px; color: #94a3b8;">姓氏:' + surnameType + (settlement.new_surname ? '(改后:' + settlement.new_surname + ')' : '') + '</div>' +
|
|
|
|
|
+ '<div style="border-top: 1px solid rgba(255,255,255,0.1); padding-top: 8px; margin-top: 4px;">' +
|
|
|
|
|
+ '<button onclick="event.stopPropagation(); showSettlementDetail(' + JSON.stringify(settlement).replace(/"/g, '"') + '); hideMarkerTooltip();" style="background: #3B82F6; color: white; border: none; padding: 4px 12px; border-radius: 4px; font-size: 11px; cursor: pointer;">查看详情</button>' +
|
|
|
|
|
+ '</div>';
|
|
|
|
|
+
|
|
|
|
|
+ const containerRect = document.getElementById('map-container').getBoundingClientRect();
|
|
|
|
|
+ tooltipDiv.style.left = (containerRect.left + e.pixel.x + 10) + 'px';
|
|
|
|
|
+ tooltipDiv.style.top = (containerRect.top + e.pixel.y - tooltipDiv.offsetHeight / 2) + 'px';
|
|
|
|
|
+ tooltipDiv.style.display = 'block';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function hideMarkerTooltip() {
|
|
|
|
|
+ if (tooltipDiv) {
|
|
|
|
|
+ tooltipDiv.style.display = 'none';
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+{% endblock %}
|