Эх сурвалжийг харах

修改耕地图层和导入导出接口,替换地图图标

DIng 3 сар өмнө
parent
commit
4ad22203a0

+ 2 - 5
api/app/model.py

@@ -114,15 +114,12 @@ def save_current_dataset(session, data_type, commit=True):
     Returns:
     int: 新保存的数据集的ID。
     """
-    current_time = datetime.datetime.now()
-    
     new_dataset = Datasets(
-        Dataset_name=f"{data_type}_dataset_{current_time:%Y%m%d_%H%M%S}",
+        Dataset_name=f"{data_type}_dataset_{datetime.datetime.now():%Y%m%d_%H%M%S}",
         Dataset_description=f"Automatically generated dataset for type {data_type}",
         Row_count=0,
         Status='pending',
-        Dataset_type=data_type,
-        Uploaded_at=current_time
+        Dataset_type=data_type
     )
 
     session.add(new_dataset)

+ 21 - 261
api/app/routes.py

@@ -13,7 +13,6 @@ from sqlalchemy.orm import sessionmaker
 import logging
 from sqlalchemy import text, select, MetaData, Table, func
 from .tasks import train_model_task
-from datetime import datetime
 
 # 配置日志
 logging.basicConfig(level=logging.DEBUG)
@@ -108,8 +107,7 @@ def upload_dataset():
             Dataset_description=dataset_description,
             Row_count=0,
             Status='Datasets_upgraded',
-            Dataset_type=dataset_type,
-            Uploaded_at=datetime.now()
+            Dataset_type=dataset_type
         )
         session.add(new_dataset)
         session.commit()
@@ -350,87 +348,48 @@ def list_tables():
 
 @bp.route('/models/<int:model_id>', methods=['GET'])
 def get_model(model_id):
-    """
-    获取单个模型信息的API接口
-    
-    @param model_id: 模型ID
-    @return: JSON响应
-    """
-    Session = sessionmaker(bind=db.engine)
-    session = Session()
-    
     try:
-        model = session.query(Models).filter_by(ModelID=model_id).first()
+        model = Models.query.filter_by(ModelID=model_id).first()
         if model:
             return jsonify({
                 'ModelID': model.ModelID,
-                'Model_name': model.Model_name,
-                'Model_type': model.Model_type,
-                'Created_at': model.Created_at.strftime('%Y-%m-%d %H:%M:%S'),
-                'Description': model.Description,
-                'Performance_score': float(model.Performance_score) if model.Performance_score else None,
-                'Data_type': model.Data_type
+                'ModelName': model.ModelName,
+                'ModelType': model.ModelType,
+                'CreatedAt': model.CreatedAt.strftime('%Y-%m-%d %H:%M:%S'),
+                'Description': model.Description
             })
         else:
-            return jsonify({'message': '未找到模型'}), 404
-            
+            return jsonify({'message': 'Model not found'}), 404
     except Exception as e:
-        logger.error(f'获取模型信息失败: {str(e)}')
-        return jsonify({'error': '服务器内部错误', 'message': str(e)}), 500
-        
-    finally:
-        session.close()
+        return jsonify({'error': 'Internal server error', 'message': str(e)}), 500
 
 
 @bp.route('/models', methods=['GET'])
 def get_all_models():
-    """
-    获取所有模型信息的API接口
-    
-    @return: JSON响应
-    """
-    Session = sessionmaker(bind=db.engine)
-    session = Session()
-    
     try:
-        models = session.query(Models).all()
+        models = Models.query.all()  # 获取所有模型数据
         if models:
             result = [
                 {
                     'ModelID': model.ModelID,
-                    'Model_name': model.Model_name,
-                    'Model_type': model.Model_type,
-                    'Created_at': model.Created_at.strftime('%Y-%m-%d %H:%M:%S'),
-                    'Description': model.Description,
-                    'Performance_score': float(model.Performance_score) if model.Performance_score else None,
-                    'Data_type': model.Data_type
+                    'ModelName': model.ModelName,
+                    'ModelType': model.ModelType,
+                    'CreatedAt': model.CreatedAt.strftime('%Y-%m-%d %H:%M:%S'),
+                    'Description': model.Description
                 }
                 for model in models
             ]
             return jsonify(result)
         else:
-            return jsonify({'message': '未找到任何模型'}), 404
-            
+            return jsonify({'message': 'No models found'}), 404
     except Exception as e:
-        logger.error(f'获取所有模型信息失败: {str(e)}')
-        return jsonify({'error': '服务器内部错误', 'message': str(e)}), 500
-        
-    finally:
-        session.close()
+        return jsonify({'error': 'Internal server error', 'message': str(e)}), 500
 
 
 @bp.route('/model-parameters', methods=['GET'])
 def get_all_model_parameters():
-    """
-    获取所有模型参数的API接口
-    
-    @return: JSON响应
-    """
-    Session = sessionmaker(bind=db.engine)
-    session = Session()
-    
     try:
-        parameters = session.query(ModelParameters).all()
+        parameters = ModelParameters.query.all()  # 获取所有参数数据
         if parameters:
             result = [
                 {
@@ -443,14 +402,9 @@ def get_all_model_parameters():
             ]
             return jsonify(result)
         else:
-            return jsonify({'message': '未找到任何参数'}), 404
-            
+            return jsonify({'message': 'No parameters found'}), 404
     except Exception as e:
-        logger.error(f'获取所有模型参数失败: {str(e)}')
-        return jsonify({'error': '服务器内部错误', 'message': str(e)}), 500
-        
-    finally:
-        session.close()
+        return jsonify({'error': 'Internal server error', 'message': str(e)}), 500
 
 
 @bp.route('/models/<int:model_id>/parameters', methods=['GET'])
@@ -861,14 +815,7 @@ def get_task_status(task_id):
 
 
 @bp.route('/delete-model/<int:model_id>', methods=['DELETE'])
-def delete_model_route(model_id):
-    # 将URL参数转换为布尔值
-    delete_dataset_param = request.args.get('delete_dataset', 'False').lower() == 'true'
-    
-    # 调用原始函数
-    return delete_model(model_id, delete_dataset=delete_dataset_param)
-
-def delete_model(model_id, delete_dataset=False):
+def delete_model(model_id):
     """
     删除指定模型的API接口
     
@@ -904,6 +851,7 @@ def delete_model(model_id, delete_dataset=False):
                 return jsonify({'error': f'删除模型文件失败: {str(e)}'}), 500
 
         # 3. 如果需要删除关联的数据集
+        delete_dataset = request.args.get('delete_dataset', 'false').lower() == 'true'
         if delete_dataset and dataset_id:
             try:
                 dataset_response = delete_dataset_endpoint(dataset_id)
@@ -929,6 +877,7 @@ def delete_model(model_id, delete_dataset=False):
                 'dataset_id': dataset_id,
                 'message': '关联数据集已删除'
             }
+            
         return jsonify(response_data), 200
         
     except Exception as e:
@@ -1445,192 +1394,3 @@ def set_current_dataset(data_type, dataset_id):
         
     finally:
         session.close()
-
-@bp.route('/get-model-history/<string:data_type>', methods=['GET'])
-def get_model_history(data_type):
-    """
-    获取模型训练历史数据的API接口
-    
-    @param data_type: 数据集类型 ('reduce' 或 'reflux')
-    @return: JSON响应,包含时间序列的模型性能数据
-    """
-    Session = sessionmaker(bind=db.engine)
-    session = Session()
-    
-    try:
-        # 查询所有自动生成的数据集,按时间排序
-        datasets = session.query(Datasets).filter(
-            Datasets.Dataset_type == data_type,
-            Datasets.Dataset_description == f"Automatically generated dataset for type {data_type}"
-        ).order_by(Datasets.Uploaded_at).all()
-        
-        history_data = []
-        for dataset in datasets:
-            # 查找对应的自动训练模型
-            model = session.query(Models).filter(
-                Models.DatasetID == dataset.Dataset_ID,
-                Models.Model_name.like(f'auto_trained_{data_type}_%')
-            ).first()
-            
-            if model and model.Performance_score is not None:
-                # 直接使用数据库中的时间,不进行格式化(保持与created_at相同的时区)
-                created_at = model.Created_at.isoformat() if model.Created_at else None
-                
-                history_data.append({
-                    'dataset_id': dataset.Dataset_ID,
-                    'row_count': dataset.Row_count,
-                    'model_id': model.ModelID,
-                    'model_name': model.Model_name,
-                    'performance_score': float(model.Performance_score),
-                    'timestamp': created_at
-                })
-        
-        # 按时间戳排序
-        history_data.sort(key=lambda x: x['timestamp'] if x['timestamp'] else '')
-        
-        # 构建返回数据,分离各个指标序列便于前端绘图
-        response_data = {
-            'data_type': data_type,
-            'timestamps': [item['timestamp'] for item in history_data],
-            'row_counts': [item['row_count'] for item in history_data],
-            'performance_scores': [item['performance_score'] for item in history_data],
-            'model_details': history_data  # 保留完整数据供前端使用
-        }
-        
-        return jsonify(response_data), 200
-        
-    except Exception as e:
-        logger.error(f'获取模型历史数据失败: {str(e)}', exc_info=True)
-        return jsonify({'error': str(e)}), 500
-        
-    finally:
-        session.close()
-
-@bp.route('/batch-delete-datasets', methods=['POST'])
-def batch_delete_datasets():
-    """
-    批量删除数据集的API接口
-    
-    @body_param dataset_ids: 要删除的数据集ID列表
-    @return: JSON响应
-    """
-    try:
-        data = request.get_json()
-        dataset_ids = data.get('dataset_ids', [])
-        
-        if not dataset_ids:
-            return jsonify({'error': '未提供数据集ID列表'}), 400
-            
-        results = {
-            'success': [],
-            'failed': [],
-            'protected': []  # 被模型使用的数据集
-        }
-        
-        for dataset_id in dataset_ids:
-            try:
-                # 调用单个删除接口
-                response = delete_dataset_endpoint(dataset_id)
-                
-                # 解析响应
-                if response[1] == 200:
-                    results['success'].append(dataset_id)
-                elif response[1] == 400 and 'models' in response[0].json:
-                    # 数据集被模型保护
-                    results['protected'].append({
-                        'id': dataset_id,
-                        'models': response[0].json['models']
-                    })
-                else:
-                    results['failed'].append({
-                        'id': dataset_id,
-                        'reason': response[0].json.get('error', '删除失败')
-                    })
-                    
-            except Exception as e:
-                logger.error(f'删除数据集 {dataset_id} 失败: {str(e)}')
-                results['failed'].append({
-                    'id': dataset_id,
-                    'reason': str(e)
-                })
-        
-        # 构建响应消息
-        message = f"成功删除 {len(results['success'])} 个数据集"
-        if results['protected']:
-            message += f", {len(results['protected'])} 个数据集被保护"
-        if results['failed']:
-            message += f", {len(results['failed'])} 个数据集删除失败"
-            
-        return jsonify({
-            'message': message,
-            'results': results
-        }), 200
-        
-    except Exception as e:
-        logger.error(f'批量删除数据集失败: {str(e)}')
-        return jsonify({'error': str(e)}), 500
-
-@bp.route('/batch-delete-models', methods=['POST'])
-def batch_delete_models():
-    """
-    批量删除模型的API接口
-    
-    @body_param model_ids: 要删除的模型ID列表
-    @query_param delete_datasets: 布尔值,是否同时删除关联的数据集,默认为False
-    @return: JSON响应
-    """
-    try:
-        data = request.get_json()
-        model_ids = data.get('model_ids', [])
-        delete_datasets = request.args.get('delete_datasets', 'false').lower() == 'true'
-        
-        if not model_ids:
-            return jsonify({'error': '未提供模型ID列表'}), 400
-            
-        results = {
-            'success': [],
-            'failed': [],
-            'datasets_deleted': []  # 如果delete_datasets为true,记录被删除的数据集
-        }
-        
-        for model_id in model_ids:
-            try:
-                # 调用单个删除接口
-                response = delete_model(model_id, delete_dataset=delete_datasets)
-                
-                # 解析响应
-                if response[1] == 200:
-                    results['success'].append(model_id)
-                    # 如果删除了关联数据集,记录数据集ID
-                    if 'dataset_info' in response[0].json:
-                        results['datasets_deleted'].append(
-                            response[0].json['dataset_info']['dataset_id']
-                        )
-                else:
-                    results['failed'].append({
-                        'id': model_id,
-                        'reason': response[0].json.get('error', '删除失败')
-                    })
-                    
-            except Exception as e:
-                logger.error(f'删除模型 {model_id} 失败: {str(e)}')
-                results['failed'].append({
-                    'id': model_id,
-                    'reason': str(e)
-                })
-        
-        # 构建响应消息
-        message = f"成功删除 {len(results['success'])} 个模型"
-        if results['datasets_deleted']:
-            message += f", {len(results['datasets_deleted'])} 个关联数据集"
-        if results['failed']:
-            message += f", {len(results['failed'])} 个模型删除失败"
-            
-        return jsonify({
-            'message': message,
-            'results': results
-        }), 200
-        
-    except Exception as e:
-        logger.error(f'批量删除模型失败: {str(e)}')
-        return jsonify({'error': str(e)}), 500

+ 10 - 0
api/app/test.py

@@ -0,0 +1,10 @@
+from flask import Flask, jsonify
+
+app = Flask(__name__)
+
+@app.route('/api/hello', methods=['GET'])
+def hello():
+    return jsonify({"message": "Hello from Flask!"}), 200
+
+if __name__ == '__main__':
+    app.run(debug=True)

+ 7 - 1
app.js

@@ -1,10 +1,16 @@
-//app.js
+import qqmap from './libs/qqmap-wx-jssdk.min.js';
+
 App({
   onLaunch: function () {
     // 展示本地存储能力
     var logs = wx.getStorageSync('logs') || []
     logs.unshift(Date.now())
     wx.setStorageSync('logs', logs)
+    this.qqmapsdk = new qqmap({
+      key: '2R4BZ-FF4RM-Q6C6U-6TCJL-O2EN5-DVFH5' // 替换为真实密钥
+    });
+    // 挂载到全局
+    wx.qqmapsdk = this.qqmapsdk;
 
     // 登录
     wx.login({

BIN
assets/taddar/地图.png


BIN
assets/taddar/登录.png


+ 1122 - 0
libs/qqmap-wx-jssdk.js

@@ -0,0 +1,1122 @@
+/**
+ * 微信小程序JavaScriptSDK
+ * 
+ * @version 1.2
+ * @date 2019-03-06
+ */
+
+var ERROR_CONF = {
+    KEY_ERR: 311,
+    KEY_ERR_MSG: 'key格式错误',
+    PARAM_ERR: 310,
+    PARAM_ERR_MSG: '请求参数信息有误',
+    SYSTEM_ERR: 600,
+    SYSTEM_ERR_MSG: '系统错误',
+    WX_ERR_CODE: 1000,
+    WX_OK_CODE: 200
+};
+var BASE_URL = 'https://apis.map.qq.com/ws/';
+var URL_SEARCH = BASE_URL + 'place/v1/search';
+var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion';
+var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/';
+var URL_CITY_LIST = BASE_URL + 'district/v1/list';
+var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren';
+var URL_DISTANCE = BASE_URL + 'distance/v1/';
+var URL_DIRECTION = BASE_URL + 'direction/v1/';
+var MODE = {
+  driving: 'driving',
+  transit: 'transit'
+};
+var EARTH_RADIUS = 6378136.49;
+var Utils = {
+  /**
+  * md5加密方法
+  * 版权所有©2011 Sebastian Tschan,https://blueimp.net
+  */
+  safeAdd(x, y) {
+    var lsw = (x & 0xffff) + (y & 0xffff);
+    var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+    return (msw << 16) | (lsw & 0xffff);
+  },
+  bitRotateLeft(num, cnt) {
+    return (num << cnt) | (num >>> (32 - cnt));
+  },
+  md5cmn(q, a, b, x, s, t) {
+    return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b);
+  },
+  md5ff(a, b, c, d, x, s, t) {
+    return this.md5cmn((b & c) | (~b & d), a, b, x, s, t);
+  },
+  md5gg(a, b, c, d, x, s, t) {
+    return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t);
+  },
+  md5hh(a, b, c, d, x, s, t) {
+    return this.md5cmn(b ^ c ^ d, a, b, x, s, t);
+  },
+  md5ii(a, b, c, d, x, s, t) {
+    return this.md5cmn(c ^ (b | ~d), a, b, x, s, t);
+  },
+  binlMD5(x, len) {
+    /* append padding */
+    x[len >> 5] |= 0x80 << (len % 32);
+    x[((len + 64) >>> 9 << 4) + 14] = len;
+
+    var i;
+    var olda;
+    var oldb;
+    var oldc;
+    var oldd;
+    var a = 1732584193;
+    var b = -271733879;
+    var c = -1732584194;
+    var d = 271733878;
+
+    for (i = 0; i < x.length; i += 16) {
+      olda = a;
+      oldb = b;
+      oldc = c;
+      oldd = d;
+
+      a = this.md5ff(a, b, c, d, x[i], 7, -680876936);
+      d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586);
+      c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819);
+      b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330);
+      a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897);
+      d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426);
+      c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341);
+      b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983);
+      a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416);
+      d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417);
+      c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063);
+      b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162);
+      a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682);
+      d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101);
+      c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290);
+      b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329);
+
+      a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510);
+      d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632);
+      c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713);
+      b = this.md5gg(b, c, d, a, x[i], 20, -373897302);
+      a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691);
+      d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083);
+      c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335);
+      b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848);
+      a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438);
+      d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690);
+      c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961);
+      b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501);
+      a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467);
+      d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784);
+      c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473);
+      b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734);
+
+      a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558);
+      d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463);
+      c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562);
+      b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556);
+      a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060);
+      d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353);
+      c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632);
+      b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640);
+      a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174);
+      d = this.md5hh(d, a, b, c, x[i], 11, -358537222);
+      c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979);
+      b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189);
+      a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487);
+      d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835);
+      c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520);
+      b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651);
+
+      a = this.md5ii(a, b, c, d, x[i], 6, -198630844);
+      d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415);
+      c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905);
+      b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055);
+      a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571);
+      d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606);
+      c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523);
+      b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799);
+      a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359);
+      d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744);
+      c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380);
+      b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649);
+      a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070);
+      d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379);
+      c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259);
+      b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551);
+
+      a = this.safeAdd(a, olda);
+      b = this.safeAdd(b, oldb);
+      c = this.safeAdd(c, oldc);
+      d = this.safeAdd(d, oldd);
+    }
+    return [a, b, c, d];
+  },
+  binl2rstr(input) {
+    var i;
+    var output = '';
+    var length32 = input.length * 32;
+    for (i = 0; i < length32; i += 8) {
+      output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff);
+    }
+    return output;
+  },
+  rstr2binl(input) {
+    var i;
+    var output = [];
+    output[(input.length >> 2) - 1] = undefined;
+    for (i = 0; i < output.length; i += 1) {
+      output[i] = 0;
+    }
+    var length8 = input.length * 8;
+    for (i = 0; i < length8; i += 8) {
+      output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32);
+    }
+    return output;
+  },
+  rstrMD5(s) {
+    return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8));
+  },
+  rstrHMACMD5(key, data) {
+    var i;
+    var bkey = this.rstr2binl(key);
+    var ipad = [];
+    var opad = [];
+    var hash;
+    ipad[15] = opad[15] = undefined;
+    if (bkey.length > 16) {
+      bkey = this.binlMD5(bkey, key.length * 8);
+    }
+    for (i = 0; i < 16; i += 1) {
+      ipad[i] = bkey[i] ^ 0x36363636;
+      opad[i] = bkey[i] ^ 0x5c5c5c5c;
+    }
+    hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8);
+    return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128));
+  },
+  rstr2hex(input) {
+    var hexTab = '0123456789abcdef';
+    var output = '';
+    var x;
+    var i;
+    for (i = 0; i < input.length; i += 1) {
+      x = input.charCodeAt(i);
+      output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f);
+    }
+    return output;
+  },
+  str2rstrUTF8(input) {
+    return unescape(encodeURIComponent(input));
+  },
+  rawMD5(s) {
+    return this.rstrMD5(this.str2rstrUTF8(s));
+  },
+  hexMD5(s) {
+    return this.rstr2hex(this.rawMD5(s));
+  },
+  rawHMACMD5(k, d) {
+    return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d));
+  },
+  hexHMACMD5(k, d) {
+    return this.rstr2hex(this.rawHMACMD5(k, d));
+  },
+
+  md5(string, key, raw) {
+    if (!key) {
+      if (!raw) {
+        return this.hexMD5(string);
+      }
+      return this.rawMD5(string);
+    }
+    if (!raw) {
+      return this.hexHMACMD5(key, string);
+    }
+    return this.rawHMACMD5(key, string);
+  },
+  /**
+   * 得到md5加密后的sig参数
+   * @param {Object} requestParam 接口参数
+   * @param {String} sk签名字符串
+   * @param {String} featrue 方法名
+   * @return 返回加密后的sig参数
+   */
+  getSig(requestParam, sk, feature, mode) {
+    var sig = null;
+    var requestArr = [];
+    Object.keys(requestParam).sort().forEach(function(key){
+      requestArr.push(key + '=' + requestParam[key]);
+    });
+    if (feature == 'search') {
+      sig = '/ws/place/v1/search?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'suggest') {
+      sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'reverseGeocoder') {
+      sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'geocoder') {
+      sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'getCityList') {
+      sig = '/ws/district/v1/list?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'getDistrictByCityId') {
+      sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'calculateDistance') {
+      sig = '/ws/distance/v1/?' + requestArr.join('&') + sk;
+    }
+    if (feature == 'direction') {
+      sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk;
+    }
+    sig = this.md5(sig);
+    return sig;
+  },
+    /**
+     * 得到终点query字符串
+     * @param {Array|String} 检索数据
+     */
+    location2query(data) {
+        if (typeof data == 'string') {
+            return data;
+        }
+        var query = '';
+        for (var i = 0; i < data.length; i++) {
+            var d = data[i];
+            if (!!query) {
+                query += ';';
+            }
+            if (d.location) {
+                query = query + d.location.lat + ',' + d.location.lng;
+            }
+            if (d.latitude && d.longitude) {
+                query = query + d.latitude + ',' + d.longitude;
+            }
+        }
+        return query;
+    },
+
+    /**
+     * 计算角度
+     */
+    rad(d) {
+      return d * Math.PI / 180.0;
+    },  
+    /**
+     * 处理终点location数组
+     * @return 返回终点数组
+     */
+    getEndLocation(location){
+      var to = location.split(';');
+      var endLocation = [];
+      for (var i = 0; i < to.length; i++) {
+        endLocation.push({
+          lat: parseFloat(to[i].split(',')[0]),
+          lng: parseFloat(to[i].split(',')[1])
+        })
+      }
+      return endLocation;
+    },
+
+    /**
+     * 计算两点间直线距离
+     * @param a 表示纬度差
+     * @param b 表示经度差
+     * @return 返回的是距离,单位m
+     */
+    getDistance(latFrom, lngFrom, latTo, lngTo) {
+      var radLatFrom = this.rad(latFrom);
+      var radLatTo = this.rad(latTo);
+      var a = radLatFrom - radLatTo;
+      var b = this.rad(lngFrom) - this.rad(lngTo);
+      var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2)));
+      distance = distance * EARTH_RADIUS;
+      distance = Math.round(distance * 10000) / 10000;
+      return parseFloat(distance.toFixed(0));
+    },
+    /**
+     * 使用微信接口进行定位
+     */
+    getWXLocation(success, fail, complete) {
+        wx.getLocation({
+            type: 'gcj02',
+            success: success,
+            fail: fail,
+            complete: complete
+        });
+    },
+
+    /**
+     * 获取location参数
+     */
+    getLocationParam(location) {
+        if (typeof location == 'string') {
+            var locationArr = location.split(',');
+            if (locationArr.length === 2) {
+                location = {
+                    latitude: location.split(',')[0],
+                    longitude: location.split(',')[1]
+                };
+            } else {
+                location = {};
+            }
+        }
+        return location;
+    },
+
+    /**
+     * 回调函数默认处理
+     */
+    polyfillParam(param) {
+        param.success = param.success || function () { };
+        param.fail = param.fail || function () { };
+        param.complete = param.complete || function () { };
+    },
+
+    /**
+     * 验证param对应的key值是否为空
+     * 
+     * @param {Object} param 接口参数
+     * @param {String} key 对应参数的key
+     */
+    checkParamKeyEmpty(param, key) {
+        if (!param[key]) {
+            var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key +'参数格式有误');
+            param.fail(errconf);
+            param.complete(errconf);
+            return true;
+        }
+        return false;
+    },
+
+    /**
+     * 验证参数中是否存在检索词keyword
+     * 
+     * @param {Object} param 接口参数
+     */
+    checkKeyword(param){
+        return !this.checkParamKeyEmpty(param, 'keyword');
+    },
+
+    /**
+     * 验证location值
+     * 
+     * @param {Object} param 接口参数
+     */
+    checkLocation(param) {
+        var location = this.getLocationParam(param.location);
+        if (!location || !location.latitude || !location.longitude) {
+            var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误');
+            param.fail(errconf);
+            param.complete(errconf);
+            return false;
+        }
+        return true;
+    },
+
+    /**
+     * 构造错误数据结构
+     * @param {Number} errCode 错误码
+     * @param {Number} errMsg 错误描述
+     */
+    buildErrorConfig(errCode, errMsg) {
+        return {
+            status: errCode,
+            message: errMsg
+        };
+    },
+
+    /**
+     * 
+     * 数据处理函数
+     * 根据传入参数不同处理不同数据
+     * @param {String} feature 功能名称
+     * search 地点搜索
+     * suggest关键词提示
+     * reverseGeocoder逆地址解析
+     * geocoder地址解析
+     * getCityList获取城市列表:父集
+     * getDistrictByCityId获取区县列表:子集
+     * calculateDistance距离计算
+     * @param {Object} param 接口参数
+     * @param {Object} data 数据
+     */
+    handleData(param,data,feature){
+      if (feature == 'search') {
+        var searchResult = data.data;
+        var searchSimplify = [];
+        for (var i = 0; i < searchResult.length; i++) {
+          searchSimplify.push({
+            id: searchResult[i].id || null,
+            title: searchResult[i].title || null,
+            latitude: searchResult[i].location && searchResult[i].location.lat || null,
+            longitude: searchResult[i].location && searchResult[i].location.lng || null,
+            address: searchResult[i].address || null,
+            category: searchResult[i].category || null,
+            tel: searchResult[i].tel || null,
+            adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null,
+            city: searchResult[i].ad_info && searchResult[i].ad_info.city || null,
+            district: searchResult[i].ad_info && searchResult[i].ad_info.district || null,
+            province: searchResult[i].ad_info && searchResult[i].ad_info.province || null
+          })
+        }
+        param.success(data, {
+          searchResult: searchResult,
+          searchSimplify: searchSimplify
+        })
+      } else if (feature == 'suggest') {
+        var suggestResult = data.data;
+        var suggestSimplify = [];
+        for (var i = 0; i < suggestResult.length; i++) {
+          suggestSimplify.push({
+            adcode: suggestResult[i].adcode || null,
+            address: suggestResult[i].address || null,
+            category: suggestResult[i].category || null,
+            city: suggestResult[i].city || null,
+            district: suggestResult[i].district || null,
+            id: suggestResult[i].id || null,
+            latitude: suggestResult[i].location && suggestResult[i].location.lat || null,
+            longitude: suggestResult[i].location && suggestResult[i].location.lng || null,
+            province: suggestResult[i].province || null,
+            title: suggestResult[i].title || null,
+            type: suggestResult[i].type || null
+          })
+        }
+        param.success(data, {
+          suggestResult: suggestResult,
+          suggestSimplify: suggestSimplify
+          })
+      } else if (feature == 'reverseGeocoder') {
+        var reverseGeocoderResult = data.result;
+        var reverseGeocoderSimplify = {
+          address: reverseGeocoderResult.address || null,
+          latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null,
+          longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null,
+          adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null,
+          city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null,
+          district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null,
+          nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null,
+          province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null,
+          street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null,
+          street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null,
+          recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null,
+          rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null
+        };
+        if (reverseGeocoderResult.pois) {//判断是否返回周边poi
+          var pois = reverseGeocoderResult.pois;
+          var poisSimplify = [];
+          for (var i = 0;i < pois.length;i++) {
+            poisSimplify.push({
+              id: pois[i].id || null,
+              title: pois[i].title || null,
+              latitude: pois[i].location && pois[i].location.lat || null,
+              longitude: pois[i].location && pois[i].location.lng || null,
+              address: pois[i].address || null,
+              category: pois[i].category || null,
+              adcode: pois[i].ad_info && pois[i].ad_info.adcode || null,
+              city: pois[i].ad_info && pois[i].ad_info.city || null,
+              district: pois[i].ad_info && pois[i].ad_info.district || null,
+              province: pois[i].ad_info && pois[i].ad_info.province || null
+            })
+          }
+          param.success(data,{
+            reverseGeocoderResult: reverseGeocoderResult,
+            reverseGeocoderSimplify: reverseGeocoderSimplify,
+            pois: pois,
+            poisSimplify: poisSimplify
+          })
+        } else {
+          param.success(data, {
+            reverseGeocoderResult: reverseGeocoderResult,
+            reverseGeocoderSimplify: reverseGeocoderSimplify
+          })
+        }
+      } else if (feature == 'geocoder') {
+        var geocoderResult = data.result;
+        var geocoderSimplify = {
+          title: geocoderResult.title || null,
+          latitude: geocoderResult.location && geocoderResult.location.lat || null,
+          longitude: geocoderResult.location && geocoderResult.location.lng || null,
+          adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null,
+          province: geocoderResult.address_components && geocoderResult.address_components.province || null,
+          city: geocoderResult.address_components && geocoderResult.address_components.city || null,
+          district: geocoderResult.address_components && geocoderResult.address_components.district || null,
+          street: geocoderResult.address_components && geocoderResult.address_components.street || null,
+          street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null,
+          level: geocoderResult.level || null
+        };
+        param.success(data,{
+          geocoderResult: geocoderResult,
+          geocoderSimplify: geocoderSimplify
+        });
+      } else if (feature == 'getCityList') {
+        var provinceResult = data.result[0];
+        var cityResult = data.result[1];
+        var districtResult = data.result[2];
+        param.success(data,{
+          provinceResult: provinceResult,
+          cityResult: cityResult,
+          districtResult: districtResult
+        });
+      } else if (feature == 'getDistrictByCityId') {
+        var districtByCity = data.result[0];
+        param.success(data, districtByCity);
+      } else if (feature == 'calculateDistance') {
+        var calculateDistanceResult = data.result.elements;  
+        var distance = [];
+        for (var i = 0; i < calculateDistanceResult.length; i++){
+          distance.push(calculateDistanceResult[i].distance);
+        }   
+        param.success(data, {
+          calculateDistanceResult: calculateDistanceResult,
+          distance: distance
+          });
+      } else if (feature == 'direction') {
+        var direction = data.result.routes;
+        param.success(data,direction);
+      } else {
+        param.success(data);
+      }
+    },
+
+    /**
+     * 构造微信请求参数,公共属性处理
+     * 
+     * @param {Object} param 接口参数
+     * @param {Object} param 配置项
+     * @param {String} feature 方法名
+     */
+    buildWxRequestConfig(param, options, feature) {
+        var that = this;
+        options.header = { "content-type": "application/json" };
+        options.method = 'GET';
+        options.success = function (res) {
+            var data = res.data;
+            if (data.status === 0) {
+              that.handleData(param, data, feature);
+            } else {
+                param.fail(data);
+            }
+        };
+        options.fail = function (res) {
+            res.statusCode = ERROR_CONF.WX_ERR_CODE;
+            param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+        };
+        options.complete = function (res) {
+            var statusCode = +res.statusCode;
+            switch(statusCode) {
+                case ERROR_CONF.WX_ERR_CODE: {
+                    param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+                    break;
+                }
+                case ERROR_CONF.WX_OK_CODE: {
+                    var data = res.data;
+                    if (data.status === 0) {
+                        param.complete(data);
+                    } else {
+                        param.complete(that.buildErrorConfig(data.status, data.message));
+                    }
+                    break;
+                }
+                default:{
+                    param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG));
+                }
+
+            }
+        };
+        return options;
+    },
+
+    /**
+     * 处理用户参数是否传入坐标进行不同的处理
+     */
+    locationProcess(param, locationsuccess, locationfail, locationcomplete) {
+        var that = this;
+        locationfail = locationfail || function (res) {
+            res.statusCode = ERROR_CONF.WX_ERR_CODE;
+            param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+        };
+        locationcomplete = locationcomplete || function (res) {
+            if (res.statusCode == ERROR_CONF.WX_ERR_CODE) {
+                param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg));
+            }
+        };
+        if (!param.location) {
+            that.getWXLocation(locationsuccess, locationfail, locationcomplete);
+        } else if (that.checkLocation(param)) {
+            var location = Utils.getLocationParam(param.location);
+            locationsuccess(location);
+        }
+    }
+};
+
+
+class QQMapWX {
+
+    /**
+     * 构造函数
+     * 
+     * @param {Object} options 接口参数,key 为必选参数
+     */
+    constructor(options) {
+        if (!options.key) {
+            throw Error('key值不能为空');
+        }
+        this.key = options.key;
+    };
+
+    /**
+     * POI周边检索
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 参数对象结构可以参考
+     * @see http://lbs.qq.com/webservice_v1/guide-search.html
+     */
+    search(options) {
+        var that = this;
+        options = options || {};
+
+        Utils.polyfillParam(options);
+
+        if (!Utils.checkKeyword(options)) {
+            return;
+        }
+
+        var requestParam = {
+            keyword: options.keyword,
+            orderby: options.orderby || '_distance',
+            page_size: options.page_size || 10,
+            page_index: options.page_index || 1,
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.address_format) {
+            requestParam.address_format = options.address_format;
+        }
+
+        if (options.filter) {
+            requestParam.filter = options.filter;
+        }
+
+        var distance = options.distance || "1000";
+        var auto_extend = options.auto_extend || 1;
+        var region = null;
+        var rectangle = null;
+
+        //判断城市限定参数
+        if (options.region) {
+          region = options.region;
+        }
+
+        //矩形限定坐标(暂时只支持字符串格式)
+        if (options.rectangle) {
+          rectangle = options.rectangle;
+        }
+
+        var locationsuccess = function (result) {        
+          if (region && !rectangle) {
+            //城市限定参数拼接
+            requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+          } else if (rectangle && !region) {
+            //矩形搜索
+            requestParam.boundary = "rectangle(" + rectangle + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+            } else {
+              requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")";
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'search');
+            }
+            }            
+            wx.request(Utils.buildWxRequestConfig(options, {
+                url: URL_SEARCH,
+                data: requestParam
+            }, 'search'));
+        };
+        Utils.locationProcess(options, locationsuccess);
+    };
+
+    /**
+     * sug模糊检索
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 参数对象结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-suggestion.html
+     */
+    getSuggestion(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (!Utils.checkKeyword(options)) {
+            return;
+        }
+
+        var requestParam = {
+            keyword: options.keyword,
+            region: options.region || '全国',
+            region_fix: options.region_fix || 0,
+            policy: options.policy || 0,
+            page_size: options.page_size || 10,//控制显示条数
+            page_index: options.page_index || 1,//控制页数
+            get_subpois : options.get_subpois || 0,//返回子地点
+            output: 'json',
+            key: that.key
+        };
+        //长地址
+        if (options.address_format) {
+          requestParam.address_format = options.address_format;
+        }
+        //过滤
+        if (options.filter) {
+          requestParam.filter = options.filter;
+        }
+        //排序
+        if (options.location) {
+          var locationsuccess = function (result) {
+            requestParam.location = result.latitude + ',' + result.longitude;
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
+            }
+            wx.request(Utils.buildWxRequestConfig(options, {
+              url: URL_SUGGESTION,
+              data: requestParam
+            }, "suggest"));      
+          };
+          Utils.locationProcess(options, locationsuccess);
+        } else {
+          if (options.sig) {
+            requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest');
+          }
+          wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_SUGGESTION,
+            data: requestParam
+          }, "suggest"));      
+        }        
+    };
+
+    /**
+     * 逆地址解析
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-gcoder.html
+     */
+    reverseGeocoder(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+        var requestParam = {
+            coord_type: options.coord_type || 5,
+            get_poi: options.get_poi || 0,
+            output: 'json',
+            key: that.key
+        };
+        if (options.poi_options) {
+            requestParam.poi_options = options.poi_options
+        }
+
+        var locationsuccess = function (result) {
+            requestParam.location = result.latitude + ',' + result.longitude;
+          if (options.sig) {
+            requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder');
+          }
+            wx.request(Utils.buildWxRequestConfig(options, {
+                url: URL_GET_GEOCODER,
+                data: requestParam
+            }, 'reverseGeocoder'));
+        };
+        Utils.locationProcess(options, locationsuccess);
+    };
+
+    /**
+     * 地址解析
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-geocoder.html
+     */
+    geocoder(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'address')) {
+            return;
+        }
+
+        var requestParam = {
+            address: options.address,
+            output: 'json',
+            key: that.key
+        };
+
+        //城市限定
+        if (options.region) {
+          requestParam.region = options.region;
+        }
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_GET_GEOCODER,
+            data: requestParam
+        },'geocoder'));
+    };
+
+
+    /**
+     * 获取城市列表
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-region.html
+     */
+    getCityList(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+        var requestParam = {
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_CITY_LIST,
+            data: requestParam
+        },'getCityList'));
+    };
+
+    /**
+     * 获取对应城市ID的区县列表
+     *
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-region.html
+     */
+    getDistrictByCityId(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'id')) {
+            return;
+        }
+
+        var requestParam = {
+            id: options.id || '',
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.sig) {
+          requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId');
+        }
+
+        wx.request(Utils.buildWxRequestConfig(options, {
+            url: URL_AREA_LIST,
+            data: requestParam
+        },'getDistrictByCityId'));
+    };
+
+    /**
+     * 用于单起点到多终点的路线距离(非直线距离)计算:
+     * 支持两种距离计算方式:步行和驾车。
+     * 起点到终点最大限制直线距离10公里。
+     *
+     * 新增直线距离计算。
+     * 
+     * @param {Object} options 接口参数对象
+     * 
+     * 请求参数结构可以参考
+     * http://lbs.qq.com/webservice_v1/guide-distance.html
+     */
+    calculateDistance(options) {
+        var that = this;
+        options = options || {};
+        Utils.polyfillParam(options);
+
+        if (Utils.checkParamKeyEmpty(options, 'to')) {
+            return;
+        }
+
+        var requestParam = {
+            mode: options.mode || 'walking',
+            to: Utils.location2query(options.to),
+            output: 'json',
+            key: that.key
+        };
+
+        if (options.from) {
+          options.location = options.from;
+        }
+
+        //计算直线距离
+        if(requestParam.mode == 'straight'){        
+          var locationsuccess = function (result) {
+            var locationTo = Utils.getEndLocation(requestParam.to);//处理终点坐标
+            var data = {
+              message:"query ok",
+              result:{
+                elements:[]
+              },
+              status:0
+            };
+            for (var i = 0; i < locationTo.length; i++) {
+              data.result.elements.push({//将坐标存入
+                distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng),
+                duration:0,
+                from:{
+                  lat: result.latitude,
+                  lng:result.longitude
+                },
+                to:{
+                  lat: locationTo[i].lat,
+                  lng: locationTo[i].lng
+                }
+              });            
+            }
+            var calculateResult = data.result.elements;
+            var distanceResult = [];
+            for (var i = 0; i < calculateResult.length; i++) {
+              distanceResult.push(calculateResult[i].distance);
+            }  
+            return options.success(data,{
+              calculateResult: calculateResult,
+              distanceResult: distanceResult
+            });
+          };
+          
+          Utils.locationProcess(options, locationsuccess);
+        } else {
+          var locationsuccess = function (result) {
+            requestParam.from = result.latitude + ',' + result.longitude;
+            if (options.sig) {
+              requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance');
+            }
+            wx.request(Utils.buildWxRequestConfig(options, {
+              url: URL_DISTANCE,
+              data: requestParam
+            },'calculateDistance'));
+          };
+
+          Utils.locationProcess(options, locationsuccess);
+        }      
+    };
+
+  /**
+   * 路线规划:
+   * 
+   * @param {Object} options 接口参数对象
+   * 
+   * 请求参数结构可以参考
+   * https://lbs.qq.com/webservice_v1/guide-road.html
+   */
+  direction(options) {
+    var that = this;
+    options = options || {};
+    Utils.polyfillParam(options);
+
+    if (Utils.checkParamKeyEmpty(options, 'to')) {
+      return;
+    }
+
+    var requestParam = {
+      output: 'json',
+      key: that.key
+    };
+
+    //to格式处理
+    if (typeof options.to == 'string') {
+      requestParam.to = options.to;
+    } else {
+      requestParam.to = options.to.latitude + ',' + options.to.longitude;
+    }
+    //初始化局部请求域名
+    var SET_URL_DIRECTION = null;
+    //设置默认mode属性
+    options.mode = options.mode || MODE.driving;
+
+    //设置请求域名
+    SET_URL_DIRECTION = URL_DIRECTION + options.mode;
+
+    if (options.from) {
+      options.location = options.from;
+    }
+
+    if (options.mode == MODE.driving) {
+      if (options.from_poi) {
+        requestParam.from_poi = options.from_poi;
+      }
+      if (options.heading) {
+        requestParam.heading = options.heading;
+      }
+      if (options.speed) {
+        requestParam.speed = options.speed;
+      }
+      if (options.accuracy) {
+        requestParam.accuracy = options.accuracy;
+      }
+      if (options.road_type) {
+        requestParam.road_type = options.road_type;
+      }
+      if (options.to_poi) {
+        requestParam.to_poi = options.to_poi;
+      }
+      if (options.from_track) {
+        requestParam.from_track = options.from_track;
+      }
+      if (options.waypoints) {
+        requestParam.waypoints = options.waypoints;
+      }
+      if (options.policy) {
+        requestParam.policy = options.policy;
+      }
+      if (options.plate_number) {
+        requestParam.plate_number = options.plate_number;
+      }
+    }
+
+    if (options.mode == MODE.transit) {
+      if (options.departure_time) {
+        requestParam.departure_time = options.departure_time;
+      }
+      if (options.policy) {
+        requestParam.policy = options.policy;
+      }
+    } 
+
+    var locationsuccess = function (result) {
+      requestParam.from = result.latitude + ',' + result.longitude;
+      if (options.sig) {
+        requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction',options.mode);
+      }
+      wx.request(Utils.buildWxRequestConfig(options, {
+        url: SET_URL_DIRECTION,
+        data: requestParam
+      }, 'direction'));
+    };
+
+    Utils.locationProcess(options, locationsuccess);
+  }
+};
+
+module.exports = QQMapWX;

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
libs/qqmap-wx-jssdk.min.js


+ 1 - 1
pages/Home/Home.wxml

@@ -16,7 +16,7 @@
     <text class="card-title">团队信息</text>
   </view>
   <view class="card" bindtap="mapView">
-    <image class="card-icon" src="/assets/taddar/团队介绍@1x.png" />
+    <image class="card-icon" src="/assets/taddar/地图.png" />
     <text class="card-title">地图展示</text>
   </view>
 </view>

+ 6 - 1
pages/Home/Home.wxss

@@ -4,7 +4,8 @@
   gap: 20px;
   padding: 20px;
   background-color: #f5f5f5;
-  margin-top: 60px;
+  margin-top: 20px;
+  padding-bottom: 70px; 
 }
 
 .card {
@@ -56,3 +57,7 @@
 .card:nth-child(4) {
   background-color: #FFF8DC; /* 团队信息卡片颜色 */
 }
+
+.card:nth-child(5) {
+  background-color: #E6E6FA; /* 团队信息卡片颜色 */
+}

+ 165 - 83
pages/Visualizatio/Visualizatio.js

@@ -2,7 +2,7 @@ Page({
   data: {
     isEditing: false,
     tableName: 'current_reduce', // 确保这个表名在页面中是有效的
-    fileFormat: 'excel', // 默认是 Excel 格式
+    fileFormat: 'Excel', // 默认是 Excel 格式
     shoopingtext: "", // 搜索框内容
     filteredRows: [], // 表格过滤后的数据
     rows: [], // 所有表格数据
@@ -82,15 +82,15 @@ Page({
     });
   },
 
-  // 下载模板函数
   onDownloadTemplate: function() {
     wx.showLoading({
       title: '下载中...',
     });
   
     const tableName = 'current_reduce';  // 或者根据需要选择表名
-    const format = 'excel';  // 或者 'csv' 作为模板格式
+    const format = 'xlsx';  // 或者 'csv' 作为模板格式
   
+    // 向后端发送请求以获取模板
     wx.request({
       url: `https://soilgd.com:5000/download_template?table=${tableName}&format=${format}`,
       method: 'GET',
@@ -114,25 +114,28 @@ Page({
             data: res.data,
             encoding: 'binary',
             success: () => {
-              wx.showToast({
-                title: '文件已保存',
-                icon: 'success'
-              });
-              // 触发文件下载
-              wx.openDocument({
-                filePath: filePath,
-                fileType: format === 'excel' ? 'xlsx' : 'csv',
-                success: () => {
-                  console.log('文件打开成功');
+              // 文件保存成功后,显示菜单
+              wx.showActionSheet({
+                itemList: ['保存文件', '分享文件'],
+                success: (res) => {
+                  if (res.tapIndex === 0) {  // 用户选择保存文件
+                    wx.showToast({
+                      title: '文件已保存',
+                      icon: 'success'
+                    });
+                  } else if (res.tapIndex === 1) {  // 用户选择分享文件
+                    this.onShareFile(filePath);
+                  }
                 },
-                fail: (error) => {
-                  console.error('文件打开失败', error);
-                  wx.showToast({
-                    title: '打开文件失败',
-                    icon: 'none'
-                  });
+                fail: (err) => {
+                  console.error('菜单显示失败', err);
                 }
               });
+  
+              // 保存文件路径以便后续使用
+              this.setData({
+                shareFilePath: filePath
+              });
             },
             fail: (err) => {
               wx.showToast({
@@ -158,82 +161,161 @@ Page({
         console.error('请求失败:', err);
       }
     });
+  },
+  
+  // 分享文件功能
+  onShareFile: function(filePath) {
+    if (!filePath) {
+      wx.showToast({
+        title: '文件不存在',
+        icon: 'none',
+      });
+      return;
+    }
+  
+    // 分享文件
+    wx.shareFileMessage({
+      filePath: filePath,
+      success: () => {
+        wx.showToast({
+          title: '文件已分享',
+          icon: 'success',
+        });
+      },
+      fail: (error) => {
+        wx.showToast({
+          title: '分享失败',
+          icon: 'none',
+        });
+        console.error('文件分享失败', error);
+      }
+    });
   },  
 
-   // 导出数据按钮点击事件
-   onExport: function () {
-    const tableName = this.data.tableName;
-    const fileFormat = this.data.fileFormat;
+  // 导出数据按钮点击事件
+onExport: function () {
+  const tableName = this.data.tableName;
+  const fileFormat = this.data.fileFormat;
 
-    wx.showLoading({
-      title: '导出中...',
-    });
+  wx.showLoading({
+    title: '导出中...',
+  });
 
-    // 向后端发送请求,获取表格数据并导出
-    wx.request({
-      url: `https://soilgd.com:5000/export_data?table=${tableName}&format=${fileFormat}`,
-      method: 'GET',
-      responseType: 'arraybuffer',  // 处理二进制文件
-      success: (res) => {
-        wx.hideLoading();
+  // 向后端发送请求,获取表格数据并导出
+  wx.request({
+    url: `https://soilgd.com:5000/export_data?table=${tableName}&format=${fileFormat}`,
+    method: 'GET',
+    responseType: 'arraybuffer',  // 处理二进制文件
+    success: (res) => {
+      wx.hideLoading();
 
-        // 确保响应体返回的数据是有效的文件
-        if (res.statusCode === 200) {
-          const fileName = `${tableName}_data.${fileFormat === 'excel' ? 'xlsx' : 'csv'}`;
-          const filePath = wx.env.USER_DATA_PATH + '/' + fileName;
+      // 确保响应体返回的数据是有效的文件
+      if (res.statusCode === 200) {
+        const fileName = `${tableName}_data.${fileFormat === 'excel' ? 'xlsx' : 'csv'}`;
+        const filePath = wx.env.USER_DATA_PATH + '/' + fileName;
 
-          // 保存文件到本地
-          wx.getFileSystemManager().writeFile({
-            filePath: filePath,
-            data: res.data,
-            encoding: 'binary',
-            success: () => {
-              wx.showToast({
-                title: '文件已保存',
-                icon: 'success'
-              });
+        // 保存文件到本地
+        wx.getFileSystemManager().writeFile({
+          filePath: filePath,
+          data: res.data,
+          encoding: 'binary',
+          success: () => {
+            // 打开文件
+wx.openDocument({
+  filePath: filePath,
+  fileType: fileFormat === 'excel' ? 'xlsx' : 'csv',
+  success: () => {
+    console.log('文件打开成功');
+  },
+  fail: (error) => {
+    console.error('文件打开失败', error);
+    wx.showToast({
+      title: '打开文件失败,文件类型不支持或微信版本过低',
+      icon: 'none'
+    });
+  }
+});
 
-              // 打开已保存的文件
-              wx.openDocument({
-                filePath: filePath,
-                fileType: fileFormat === 'excel' ? 'xlsx' : 'csv',
-                success: () => {
-                  console.log('文件打开成功');
-                },
-                fail: (error) => {
-                  console.error('文件打开失败', error);
-                  wx.showToast({
-                    title: '打开文件失败',
-                    icon: 'none'
-                  });
-                }
-              });
-            },
-            fail: (err) => {
-              wx.showToast({
-                title: '文件保存失败',
-                icon: 'none'
-              });
-              console.error('文件保存失败', err);
-            }
-          });
-        } else {
-          wx.showToast({
-            title: '导出失败,请重试',
-            icon: 'none'
-          });
-        }
-      },
-      fail: (err) => {
-        wx.hideLoading();
+            // 在文件数据右上角显示菜单
+            this.setData({
+              showMenu: true,  // 显示右上角菜单
+              filePath: filePath  // 保存文件路径
+            });
+          },
+          fail: (err) => {
+            wx.showToast({
+              title: '文件保存失败',
+              icon: 'none'
+            });
+            console.error('文件保存失败', err);
+          }
+        });
+      } else {
         wx.showToast({
-          title: '请求失败,请重试',
+          title: '导出失败,请重试',
           icon: 'none'
         });
-        console.error('请求失败:', err);
       }
+    },
+    fail: (err) => {
+      wx.hideLoading();
+      wx.showToast({
+        title: '请求失败,请重试',
+        icon: 'none'
+      });
+      console.error('请求失败:', err);
+    }
+  });
+},
+
+// 显示右上角菜单
+onShowMenu: function () {
+  wx.showActionSheet({
+    itemList: ['保存文件', '分享文件'],
+    success: (res) => {
+      if (res.tapIndex === 0) {  // 用户选择保存文件
+        wx.showToast({
+          title: '文件已保存',
+          icon: 'success'
+        });
+      } else if (res.tapIndex === 1) {  // 用户选择分享文件
+        this.onShareFile(this.data.filePath);  // 调用分享文件功能
+      }
+    },
+    fail: (err) => {
+      console.error('菜单显示失败', err);
+    }
+  });
+},
+
+// 分享文件功能
+onShareFile: function(filePath) {
+  if (!filePath) {
+    wx.showToast({
+      title: '文件不存在',
+      icon: 'none',
     });
-  },
+    return;
+  }
+
+  // 分享文件
+  wx.shareFileMessage({
+    filePath: filePath,
+    success: () => {
+      wx.showToast({
+        title: '文件已分享',
+        icon: 'success',
+      });
+    },
+    fail: (error) => {
+      wx.showToast({
+        title: '分享失败',
+        icon: 'none',
+      });
+      console.error('文件分享失败', error);
+    }
+  });
+},
 
   // 导入数据功能
   onImport: function () {

+ 107 - 144
pages/Visualization/Visualization.js

@@ -93,61 +93,39 @@ Page({
 
   const tableName = 'current_reflux';  // 或者根据需要选择表名
   const format = 'excel';  // 或者 'csv' 作为模板格式
+  const timestamp = new Date().getTime();
+  const fileName = `${tableName}_template_${timestamp}.${format}`;
+  
+  // 使用服务器的实际地址替换这里的URL
+  const downloadUrl = `https://soilgd.com:5000/download_template?table=${tableName}&format=${format}`;
 
-  wx.request({
-    url: `https://soilgd.com:5000/download_template?table=${tableName}&format=${format}`,
-    method: 'GET',
-    responseType: 'arraybuffer',  // 处理二进制文件
+  wx.downloadFile({
+    url: downloadUrl,
     success: (res) => {
       wx.hideLoading();
-
-      // 确保响应体返回的数据是有效的文件
       if (res.statusCode === 200) {
-        const fileType = format === 'excel' ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : 'text/csv';
-
-        // 生成唯一的文件名,添加时间戳以确保唯一性
-        const timestamp = new Date().getTime();
-        const fileName = `${tableName}_template_${timestamp}.${format}`;
-        const filePath = wx.env.USER_DATA_PATH + '/' + fileName;
-
-        // 使用文件系统管理器保存文件
-        const fs = wx.getFileSystemManager();
-        fs.writeFile({
-          filePath: filePath,
-          data: res.data,
-          encoding: 'binary',
+        // 文件下载成功后,尝试打开文件
+        wx.openDocument({
+          filePath: res.tempFilePath, // 使用临时文件路径
+          fileType: format === 'excel' ? 'xlsx' : 'csv',
           success: () => {
+            console.log('文件打开成功');
             wx.showToast({
-              title: '文件已保存',
+              title: '文件已打开',
               icon: 'success'
             });
-            // 触发文件下载
-            wx.openDocument({
-              filePath: filePath,
-              fileType: format === 'excel' ? 'xlsx' : 'csv',
-              success: () => {
-                console.log('文件打开成功');
-              },
-              fail: (error) => {
-                console.error('文件打开失败', error);
-                wx.showToast({
-                  title: '打开文件失败',
-                  icon: 'none'
-                });
-              }
-            });
           },
-          fail: (err) => {
+          fail: (error) => {
+            console.error('文件打开失败', error);
             wx.showToast({
-              title: '文件保存失败',
+              title: '打开文件失败',
               icon: 'none'
             });
-            console.error('文件保存失败', err);
           }
         });
       } else {
         wx.showToast({
-          title: '下载失败',
+          title: '下载失败,请检查网络或重试',
           icon: 'none'
         });
       }
@@ -161,68 +139,47 @@ Page({
       console.error('请求失败:', err);
     }
   });
-},  
+},
 
    // 导出数据按钮点击事件
    onExport: function () {
     const tableName = this.data.tableName;
     const fileFormat = this.data.fileFormat;
-
+  
     wx.showLoading({
       title: '导出中...',
     });
-
+  
     // 向后端发送请求,获取表格数据并导出
-    wx.request({
-      url: `https://soilgd.com:5000/export_data?table=${tableName}&format=${fileFormat}`,
-      method: 'GET',
-      responseType: 'arraybuffer',  // 处理二进制文件
+    const downloadUrl = `https://soilgd.com:5000/export_data?table=${tableName}&format=${fileFormat}`;
+  
+    wx.downloadFile({
+      url: downloadUrl,
       success: (res) => {
         wx.hideLoading();
-
-        // 确保响应体返回的数据是有效的文件
         if (res.statusCode === 200) {
-          const fileName = `${tableName}_data.${fileFormat === 'excel' ? 'xlsx' : 'csv'}`;
-          const filePath = wx.env.USER_DATA_PATH + '/' + fileName;
-
-          // 保存文件到本地
-          wx.getFileSystemManager().writeFile({
-            filePath: filePath,
-            data: res.data,
-            encoding: 'binary',
+          // 文件下载成功后,尝试打开文件
+          wx.openDocument({
+            filePath: res.tempFilePath, // 使用临时文件路径
+            fileType: fileFormat === 'excel' ? 'xlsx' : 'csv',
             success: () => {
+              console.log('文件打开成功');
               wx.showToast({
-                title: '文件已保存',
+                title: '文件已打开',
                 icon: 'success'
               });
-
-              // 打开已保存的文件
-              wx.openDocument({
-                filePath: filePath,
-                fileType: fileFormat === 'excel' ? 'xlsx' : 'csv',
-                success: () => {
-                  console.log('文件打开成功');
-                },
-                fail: (error) => {
-                  console.error('文件打开失败', error);
-                  wx.showToast({
-                    title: '打开文件失败',
-                    icon: 'none'
-                  });
-                }
-              });
             },
-            fail: (err) => {
+            fail: (error) => {
+              console.error('文件打开失败', error);
               wx.showToast({
-                title: '文件保存失败',
+                title: '打开文件失败',
                 icon: 'none'
               });
-              console.error('文件保存失败', err);
             }
           });
         } else {
           wx.showToast({
-            title: '导出失败,请重试',
+            title: '导出失败,请检查网络或重试',
             icon: 'none'
           });
         }
@@ -239,76 +196,82 @@ Page({
   },
   
   // 导入数据功能
-  onImport: function () {
-    wx.chooseMessageFile({
-      count: 1,
-      type: 'file',
-      extension: ['xlsx', 'csv'], // 允许上传的文件类型
-      success: (res) => {
-        const filePath = res.tempFiles[0].path;
-        const fileName = res.tempFiles[0].name;
-  
-        wx.showLoading({ title: '上传中...' });
-  
-        // 上传文件到后端
-        wx.uploadFile({
-          url: 'https://soilgd.com:5000/import_data', // 后端接口
-          filePath: filePath,
-          name: 'file',
-          formData: {
-            table: this.data.tableName, // 表名
-            fileName: fileName
-          },
-          success: (res) => {
-            wx.hideLoading();
-            const responseData = JSON.parse(res.data);
-  
-            if (responseData.success) {
-              // 获取后端返回的结果
-              const { total_data, new_data, duplicate_data, duplicate_details } = responseData;
-  
-              // 显示导入结果
-              wx.showModal({
-                title: '导入结果',
-                content: `总数据: ${total_data} 条\n新增: ${new_data} 条\n重复: ${duplicate_data} 条`,
-                success: () => {
-                  // 如果有重复数据,跳转到重复数据详情页面
-                  if (duplicate_data > 0) {
-                    wx.navigateTo({
-                      url: '/pages/duplicateDetails/duplicateDetails', // 重复数据详情页面
-                      success: (page) => {
-                        // 将重复数据传递给详情页面
-                        page.setData({ duplicateDetails: duplicate_details });
-                      }
-                    });
-                  }
-                }
-              });
-  
-              // 重新加载数据
-              this.LoadData();
-            } else {
-              wx.showToast({
-                title: responseData.message || '导入失败,请检查文件格式',
-                icon: 'none'
-              });
-            }
-          },
-          fail: (err) => {
-            wx.hideLoading();
+onImport: function () {
+  wx.chooseMessageFile({
+    count: 1,
+    type: 'file',
+    extension: ['xlsx', 'csv'], // 允许上传的文件类型
+    success: (res) => {
+      const filePath = res.tempFiles[0].path;
+      const fileName = res.tempFiles[0].name;
+      const fileSize = (res.tempFiles[0].size / 1024).toFixed(2); // 文件大小以KB为单位
+
+      // 显示所选文件信息
+      this.setData({
+        selectedFileInfo: `已选择文件:${fileName} (${fileSize} KB)`
+      });
+
+      wx.showLoading({ title: '上传中...' });
+
+      // 使用服务器的 URL 替代本地地址或Ngrok地址
+      const uploadUrl = 'https://soilgd.com:5000/import_data'; // 更新为您实际的服务器地址和端口
+
+      // 上传文件到后端
+      wx.uploadFile({
+        url: 'https://soilgd.com:5000/import_data', // 后端接口
+        filePath: filePath,
+        name: 'file',
+        formData: {
+          table: this.data.tableName, // 表名
+          fileName: fileName
+        },
+        success: (uploadRes) => {
+          wx.hideLoading();
+          let responseData;
+          try {
+            responseData = JSON.parse(uploadRes.data);
+          } catch (e) {
+            console.error('Failed to parse response data:', e);
             wx.showToast({
-              title: '导入失败,请重试',
+              title: '服务器响应解析失败',
               icon: 'none'
             });
-            console.error('文件上传失败:', err);
+            return;
           }
-        });
-      },
-      fail: (err) => {
-        console.error('文件选择失败:', err);
-      }
-    });
-  },  
+
+          if (responseData.success) {
+            const { total_data, new_data, duplicate_data, duplicate_details } = responseData;
+
+            // 显示导入结果
+            wx.showModal({
+              title: '导入结果',
+              content: `总数据: ${total_data} 条\n新增: ${new_data} 条\n重复: ${duplicate_data} 条`,
+            });
+
+            // 重新加载数据
+            this.LoadData();
+          } else {
+            wx.showToast({
+              title: responseData.message || '导入失败,请检查文件格式',
+              icon: 'none'
+            });
+          }
+        },
+        fail: (err) => {
+          wx.hideLoading();
+          wx.showToast({
+            title: '导入失败,请重试',
+            icon: 'none'
+          });
+          console.error('文件上传失败:', err);
+        }
+      });
+    },
+    fail: (err) => {
+      console.error('文件选择失败:', err);
+    }
+  });
+},
 
   // 新增按钮点击,显示新增弹窗
 onAdd: function() {
@@ -409,7 +372,7 @@ onSubmitAdd: function() {
     },
     success: (res) => {
       // 判断返回的状态码,进行处理
-      if (res.statusCode === 201 && res.data.success) {
+      if (res.statusCode === 200 && res.data.success) {
         this.setData({
           rows: [...this.data.rows, newRow],
           showAddModal: false

+ 21 - 8
pages/admin/admin.wxml

@@ -1,9 +1,22 @@
 <view class="container">
-  <input class="input" bindinput="inputUsername" placeholder="请输入用户名" />
-  <input class="input" bindinput="inputPassword" placeholder="请输入密码" type="password" />
-  <button class="btn" bindtap="login">登录</button>
-  <text class="error">{{errorMessage}}</text>
-</view>
-
-<!-- 底部导航栏 -->
-<nav-tabar selected="{{selected}}"></nav-tabar>
+  <image class="background-image" src="/assets/taddar/登录.png"></image>
+  <view class="form-container">
+    <!-- 欢迎语 -->
+    <text class="welcome-message">欢迎来到登录</text>
+    
+    <!-- 用户名输入框 -->
+    <input class="input" bindinput="inputUsername" placeholder="请输入您的用户名" />
+    
+    <!-- 密码输入框 -->
+    <input class="input" bindinput="inputPassword" placeholder="请输入您的密码" type="password" />
+    
+    <!-- 登录按钮 -->
+    <button class="btn" bindtap="login">登录</button>
+    
+    <!-- 错误消息 -->
+    <text class="error">{{errorMessage}}</text>
+  </view>
+  
+  <!-- 底部导航栏 -->
+  <nav-tabar selected="{{selected}}"></nav-tabar>
+</view>

+ 58 - 26
pages/admin/admin.wxss

@@ -1,42 +1,74 @@
+/* 整体容器 */
 .container {
   display: flex;
   flex-direction: column;
-  align-items: center;
   justify-content: center;
-  padding: 50px 40px;
-  height: 50vh; /* 使容器占据整个屏幕高度 */
+  align-items: center;
+  height: 550px;
+  background-size: cover;
+  position: relative; /* 确保背景图在表单之下 */
 }
 
-.input {
+/* 背景图片 */
+.background-image {
+  position: absolute;
+  top: 0;
+  left: 0;
   width: 100%;
-  height: 30px;
+  height: 100%;
+  object-fit: cover;
+  z-index: -1; /* 将背景图置于底层 */
+}
+
+/* 表单容器 */
+.form-container {
+  display: flex;
+  flex-direction: column;
+  align-items: center; /* 水平居中 */
+  justify-content: center; /* 垂直居中,如果需要的话 */
+  width: 90%;
+  max-width: 400px;
+  padding: 20px;
+  border-radius: 10px;
+  box-shadow: 0 4px 8px rgba(0, 0, 0, .1);
+  background-color: rgba(255, 255, 255, 0.8); /* 半透明背景 */
+}
+
+/* 欢迎语 */
+.welcome-message {
+  font-size: 20px;
+  text-align: center; /* 文本内部居中 */
+  margin-bottom: 20px;
+}
+
+/* 输入框 */
+.input {
+  width: 90%;
   margin-bottom: 10px;
-  padding: 0 20px;
-  border: 1px solid #ddd;
-  border-radius: var(--border-radius, 10px);
-  box-sizing: border-box;
+  padding: 10px;
+  border: 1px solid #ccc;
+  border-radius: 5px;
+  font-size: 16px;
 }
 
+/* 登录按钮 */
 .btn {
-  width: 30%;            /* 按钮占满宽度 */
-  height: 40px;
-  padding: 1px 0;        /* 按钮内边距,控制高度 */
-  margin: 1px 0;         /* 按钮间距 */
-  background-color: #3EC01E; /* 按钮背景颜色 */
-  color: white;           /* 按钮文字颜色 */
-  font-size: 16px;        /* 按钮文字大小 */
-  border: none;           /* 去除按钮边框 */
-  outline: none;          /* 去除焦点边框 */
-  text-align: center;     /* 文字居中 */
-  border-radius: 5px;     /* 圆角效果 */
+  width: 100%;
+  padding: 10px;
+  color: white;
+  background-color: #3EC01E;
+  border: none;
+  border-radius: 5px;
+  transition: background-color .3s;
 }
 
-.btn:active {
-  background-color: var(--active-color, #128c13);
-  box-shadow: 0 2rpx 5rpx rgba(0, 0, 0, 0.2);  /* 按钮点击时阴影变化 */
+.btn:hover {
+  background-color: #179414;
 }
 
-.btn:hover {
-  background-color: var(--hover-color, #16b818);
-  cursor: pointer;  /* 鼠标悬浮时显示手型 */
+/* 错误消息 */
+.error {
+  color: red;
+  text-align: center;
+  margin-top: 10px;
 }

+ 1 - 1
shoping/Home/Home.wxml

@@ -16,7 +16,7 @@
     <text class="card-title">团队信息</text>
   </view>
   <view class="card" bindtap="mapView">
-    <image class="card-icon" src="/assets/taddar/团队介绍@1x.png" />
+    <image class="card-icon" src="/assets/taddar/地图.png" />
     <text class="card-title">地图展示</text>
   </view>
 </view>

+ 7 - 2
shoping/Home/Home.wxss

@@ -4,7 +4,8 @@
   gap: 20px;
   padding: 20px;
   background-color: #f5f5f5;
-  margin-top: 60px;
+  margin-top: 20px;
+  padding-bottom: 70px; 
 }
 
 .card {
@@ -54,5 +55,9 @@
 }
 
 .card:nth-child(4) {
-  background-color: #FFD700; /* 团队信息卡片颜色 */
+  background-color: #FFF8DC; /* 团队信息卡片颜色 */
 }
+
+.card:nth-child(5) {
+  background-color: #E6E6FA; /* 团队信息卡片颜色 */
+}

+ 36 - 43
shoping/Model Selection/Model Selection.wxss

@@ -1,65 +1,58 @@
 .container {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
   padding: 20px;
-  background-color: #f8f8f8;
-  min-height: 100vh;
+  background-color: #f5f5f5;
+  margin-top: 60px;
 }
 
-.picker {
-  margin-bottom: 20px;
-  background-color: #fff;
-  padding: 10px;
-  border-radius: 5px;
-  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+.card {
+  display: flex; /* 使用 flexbox 布局 */
+  align-items: center;
+  width: 90%;
+  background-color: #ffffff;
+  padding: 20px;
+  border-radius: 12px;
+  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+  transition: transform 0.2s ease;
+  cursor: pointer;
 }
 
-.picker-label {
-  font-size: 16px;
-  color: #333;
+.card:hover {
+  transform: scale(1.05);
 }
 
-.picker-selected {
-  font-size: 16px;
-  color: #007aff;
-  margin-top: 8px;
+.card-icon {
+  width: 40px;
+  height: 40px;
+  margin-bottom: 20px;
+  margin-right: 90px; /* 与内容保持间距 */
 }
 
-.model-item {
-  margin-bottom: 15px;
-  background-color: #fff;
-  padding: 10px;
-  border-radius: 5px;
-  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
+.card-content {
+  flex: 1; /* 占据剩余空间,保证布局一致 */
+  text-align: center; /* 内容水平居中 */
 }
 
-.model-item-label {
-  display: flex;
-  align-items: center;
-  font-size: 14px;
+.card-title {
+  font-size: 18px;
+  font-weight: bold;
   color: #333;
 }
 
-.model-text {
-  margin-left: 10px;
-  font-size: 14px;
-  color: #666;
+.card:nth-child(1) {
+  background-color: #FFDDC1; /* 软件简介卡片颜色 */
 }
 
-.submit-btn {
-  margin-top: 20px;
-  background-color: #1AAD19;
-  color: white;
-  padding: 1px;
-  border-radius: 5px;
-  width: 50%;
-  font-size: 16px;
-  text-align: center;
-  cursor: pointer;
+.card:nth-child(2) {
+  background-color: #C1E1FF; /* 项目简介卡片颜色 */
 }
 
-.submit-btn:hover {
-  background-color: #3EC01E;
+.card:nth-child(3) {
+  background-color: #D4F4DD; /* 研究成果卡片颜色 */
 }
 
-.submit-btn:active {
-  background-color: #1AAD19;
+.card:nth-child(4) {
+  background-color: #FFD700; /* 团队信息卡片颜色 */
 }

+ 156 - 9
shoping/mapView/mapView.js

@@ -1,9 +1,13 @@
+let lastTapTime = 0;
 Page({
   data: {
-    latitude: 25.040415, // 默认中心纬度
+    latitude: 25.470415, // 默认中心纬度
     longitude: 110.273511, // 默认中心经度
     scale: 5,
-    markers: [] // 初始化为空数组,后续动态填充
+    markers: [], // 初始化为空数组,后续动态填充
+    showOverlay: false, // 是否显示图层
+    mapContext: null, // 地图上下文对象
+    excelData: []
   },
 
   onLoad() {
@@ -66,6 +70,10 @@ Page({
       { 土壤编号: "土87", 地点: "江西省红壤及种质资源研究所(进贤基地)1", dust_emissions: 4.55, longitude: 116.17, latitude: 28.34 },
       { 土壤编号: "土88", 地点: "江西省红壤及种质资源研究所(进贤基地)2", dust_emissions: 4.99333333333333, longitude: 116.17, latitude: 28.34 }
     ];
+    
+    // 保存数据到data和实例变量
+    this.setData({ excelData });
+    this.excelData = excelData;
 
     // 动态生成 markers 数据
     const markers = excelData.map((item, index) => ({
@@ -92,24 +100,163 @@ Page({
     }));
 
     // 更新 markers 数据
-    this.setData({ markers });
+    this.setData({ 
+      markers,
+      mapContext: wx.createMapContext("map", this) 
+    });
+  },
+
+  // 切换图层显示
+  toggleOverlay(e) {
+
+    const showOverlay = e.detail.value;
+    this.setData({ showOverlay });
+    if (showOverlay) {
+      console.log("准备添加地面覆盖物");
+      // 添加地面覆盖物
+      this.data.mapContext.addGroundOverlay({
+        id: 1, // 覆盖物 ID
+        src: 'https://soilgd.com/images/farmland_cut.png',// 图片路径
+        bounds: {
+          southwest: { latitude: 18.03, longitude: 104.25 }, // 图左下角坐标
+          northeast: { latitude: 31.26, longitude: 119.86 }  // 图片右上角坐标
+        },
+        opacity: 0.7, // 图片透明度
+        zIndex: 999, // 设置较高的 zIndex
+        success: (res) => {
+          console.log('添加地面覆盖物成功', res);
+        },
+        fail: (err) => {
+          console.error('添加地面覆盖物失败', err);
+        }
+      });
+    } else {
+      console.log("准备移除地面覆盖物");
+      // 移除地面覆盖物
+      this.data.mapContext.removeGroundOverlay({
+        id: 1, // 覆盖物 ID
+        success: (res) => {
+          console.log('移除地面覆盖物成功', res);
+        },
+        fail: (err) => {
+          console.error('移除地面覆盖物失败', err);
+        }
+      });
+    }
   },
 
-  onMarkerTap(e) {
-    console.log("Marker clicked:", e.detail.markerId);
+   onMarkerTap(e) {
     const markerId = e.detail.markerId;
+    
+    // 不处理临时标记点击
+    if (markerId === 0) return;
+
     const markers = this.data.markers.map(marker => {
       if (marker.id === markerId) {
-        marker.callout.display = "ALWAYS"; // 显示气泡
-      } else {
-        marker.callout.display = "NEVER"; // 隐藏其他气泡
+        marker.callout.display = "ALWAYS";
+      } else if (marker.id !== 0) { // 保持临时标记显示
+        marker.callout.display = "NEVER";
       }
       return marker;
     });
+    
     this.setData({ markers });
   },
 
   onCalloutTap(e) {
     console.log("Callout clicked:", e.detail.markerId);
-  }
+  },
+
+  handleMapTap(e) {
+    const now = Date.now();
+    if (now - lastTapTime < 1000) return;
+    lastTapTime = now;
+    
+    const { latitude, longitude } = e.detail;
+    
+    wx.qqmapsdk.reverseGeocoder({
+      location: { latitude, longitude },
+      success: (res) => {
+        const address = res.result.address_component;
+        const locationDesc = res.result.location_description || '';
+  
+        // 多维度判断陆地条件
+        const isValidLand = (
+          address.nation === '中国' &&
+          !['香港', '澳门', '台湾'].includes(address.province) &&
+          address.city !== '三沙市' && // 排除南海海域行政中心
+          !locationDesc.includes('海域') && // 排除描述含海域的关键词
+          address.district !== '' // 排除无区县信息区域
+        );
+  
+        if (isValidLand) {
+          console.log('有效陆地点击', latitude, longitude);
+          let minDistance = Infinity;
+          let closestPoint = null;
+          
+          this.excelData.forEach(item => {
+            const distance = this.calculateDistance(
+              latitude, longitude,
+              item.latitude, item.longitude
+            );
+            
+            if (distance < minDistance) {
+              minDistance = distance;
+              closestPoint = item;
+            }
+          });
+
+          // 创建临时标记
+          const tempMarker = {
+            id: 0, // 使用特殊ID标识临时标记
+            latitude,
+            longitude,
+            iconPath: "../../assets/taddar/marker.png", // 建议使用不同图标
+            width: 40,
+            height: 40,
+            callout: {
+              content: `经度: ${longitude.toFixed(4)}\n纬度: ${latitude.toFixed(4)}\npH值: ${closestPoint ? closestPoint.dust_emissions.toFixed(2) : '无数据'}`,
+              display: "ALWAYS",
+              fontSize: 14,
+              color: "#333",
+              bgColor: "#fff",
+              borderColor: "#07c160",
+              borderRadius: 8,
+              borderWidth: 2,
+              padding: 12,
+              anchorY: -10
+            }
+          };
+
+          // 更新标记数组(保留原有标记,替换临时标记)
+          const markers = this.data.markers
+            .filter(m => m.id !== 0)
+            .concat(tempMarker);
+
+          this.setData({ markers });
+        } else {
+          wx.showToast({ 
+            title: '非有效陆地区域', 
+            icon: 'none' 
+          });
+        }
+      },
+      fail: (err) => console.error('接口错误:', err)
+    });
+  },
+
+  // 新增距离计算方法
+  calculateDistance(lat1, lng1, lat2, lng2) {
+    const rad = (angle) => angle * Math.PI / 180;
+    const R = 6371; // 地球半径(千米)
+    
+    const dLat = rad(lat2 - lat1);
+    const dLng = rad(lng2 - lng1);
+    
+    const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
+              Math.cos(rad(lat1)) * Math.cos(rad(lat2)) *
+              Math.sin(dLng/2) * Math.sin(dLng/2);
+    
+    return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
+  },
 });

+ 5 - 0
shoping/mapView/mapView.json

@@ -1,5 +1,10 @@
 {
   "usingComponents": {},
+  "permission": {
+      "scope.userLocation": {
+        "desc": "你的位置信息将用于地图展示"
+      }
+  },
   "navigationBarTitleText": "地图展示",
   "navigationBarBackgroundColor": "#dbdbdb",  
   "navigationBarTextStyle": "black"  

+ 21 - 10
shoping/mapView/mapView.wxml

@@ -1,10 +1,21 @@
-<map 
-  id="map" 
-  latitude="{{latitude}}" 
-  longitude="{{longitude}}" 
-  markers="{{markers}}" 
-  bindmarkertap="onMarkerTap" 
-  bindcallouttap="onCalloutTap"
-  scale="{{scale}}" 
-  style="width: 100%; height: 100vh;">
-</map>
+<view>
+  <!-- 地图组件 -->
+  <map
+    id="map"
+    latitude="{{latitude}}"
+    longitude="{{longitude}}"
+    scale="{{scale}}"
+    ground-overlays="{{groundOverlays}}"
+    markers="{{markers}}"
+    bindtap="handleMapTap"
+    bindcallouttap="onCalloutTap"
+    bindmarkertap="onMarkerTap"
+    style="width: 100%; height: 80vh;">
+  </map>
+
+  <!-- 控制图层显示的开关 -->
+  <view class="switch-container">
+    <switch checked="{{showOverlay}}" bindchange="toggleOverlay" />
+    <text>显示耕地图层</text>
+  </view>
+</view>

+ 11 - 1
shoping/mapView/mapView.wxss

@@ -1 +1,11 @@
-/* shoping/mapView.wxss */
+.switch-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: 20px;
+}
+
+.switch-container text {
+  margin-left: 10px;
+  font-size: 16px;
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно