DIng 2 hónapja
szülő
commit
21d0882932
100 módosított fájl, 1382 hozzáadás és 137 törlés
  1. BIN
      api/SoilAcidification.db
  2. 442 0
      api/api_db.py
  3. 212 137
      api/app/routes.py
  4. BIN
      api/pkl/rf_model_0104_1145.pkl
  5. BIN
      api/pkl/rf_model_0104_1149.pkl
  6. BIN
      api/pkl/rf_model_0104_1237.pkl
  7. BIN
      api/pkl/rf_model_0104_1259.pkl
  8. BIN
      api/pkl/rf_model_0104_1415.pkl
  9. BIN
      api/pkl/rf_model_0104_1418.pkl
  10. BIN
      api/pkl/rf_model_0104_1420.pkl
  11. BIN
      api/pkl/rf_model_0111_2235.pkl
  12. BIN
      api/pkl/rf_model_0113_1421.pkl
  13. BIN
      api/pkl/rf_model_0118_2214.pkl
  14. BIN
      api/pkl/rf_model_0223_1714.pkl
  15. BIN
      api/pkl/rf_model_0224_1427.pkl
  16. BIN
      api/pkl/rf_model_0224_1510.pkl
  17. BIN
      api/pkl/rf_model_0227_1207.pkl
  18. BIN
      api/pkl/rf_model_0227_1208.pkl
  19. BIN
      api/pkl/rf_model_0227_1448.pkl
  20. BIN
      api/pkl/rf_model_0227_1453.pkl
  21. BIN
      api/pkl/rf_model_0227_1457.pkl
  22. BIN
      api/pkl/rf_model_0227_1501.pkl
  23. BIN
      api/pkl/rf_model_0227_1510.pkl
  24. BIN
      api/pkl/rf_model_0227_1511.pkl
  25. BIN
      api/pkl/rf_model_0227_1512.pkl
  26. BIN
      api/pkl/rf_model_0227_1514.pkl
  27. BIN
      api/pkl/rf_model_0227_1515.pkl
  28. BIN
      api/pkl/rf_model_0227_1521.pkl
  29. BIN
      api/pkl/rf_model_0227_1522.pkl
  30. BIN
      api/pkl/rf_model_0227_1530.pkl
  31. BIN
      api/pkl/rf_model_0227_2348.pkl
  32. BIN
      api/pkl/rf_model_0228_1149.pkl
  33. BIN
      api/pkl/rf_model_0301_1437.pkl
  34. BIN
      api/pkl/rf_model_0301_2213.pkl
  35. BIN
      api/pkl/rf_model_0301_2231.pkl
  36. BIN
      api/pkl/rf_model_0307_1728.pkl
  37. BIN
      api/uploads/02093a92-0220-47fb-8105-44bf04d754df.png
  38. BIN
      api/uploads/05da5c85-46d1-43f8-bc7c-7086af62f71f.png
  39. BIN
      api/uploads/10c81720-e713-4673-9df3-57e3c60c6eab.png
  40. BIN
      api/uploads/1de04420-9a47-4f85-a0bb-c27eaa79c263.png
  41. BIN
      api/uploads/43d2f03e-1dd1-4f8e-a813-20b9d184c44b.png
  42. BIN
      api/uploads/6e192688-76cc-4694-90e6-875434d258a7.png
  43. BIN
      api/uploads/977c4192-b373-4615-93b2-7f0abb4e0694.png
  44. BIN
      api/uploads/a32557e1-4e2e-4ae6-9395-1a86258ff91d.png
  45. BIN
      api/uploads/bd56314c-9543-42ff-85c1-e1e83e45756b.png
  46. BIN
      api/uploads/c49dbcb0-a7c9-4bd6-9e7c-c72659090005.png
  47. BIN
      api/uploads/datasets/dataset_29.xlsx
  48. BIN
      api/uploads/datasets/dataset_30.xlsx
  49. BIN
      api/uploads/datasets/dataset_31.xlsx
  50. BIN
      api/uploads/datasets/dataset_32.xlsx
  51. BIN
      api/uploads/datasets/dataset_33.xlsx
  52. BIN
      api/uploads/datasets/dataset_34.xlsx
  53. BIN
      api/uploads/datasets/dataset_35.xlsx
  54. BIN
      api/uploads/datasets/dataset_36.xlsx
  55. BIN
      api/uploads/datasets/dataset_37.xlsx
  56. BIN
      api/uploads/datasets/dataset_38.xlsx
  57. BIN
      api/uploads/datasets/dataset_39.xlsx
  58. BIN
      api/uploads/datasets/dataset_40.xlsx
  59. BIN
      api/uploads/datasets/dataset_41.xlsx
  60. BIN
      api/uploads/datasets/dataset_42.xlsx
  61. BIN
      api/uploads/datasets/dataset_43.xlsx
  62. BIN
      api/uploads/datasets/dataset_44.xlsx
  63. BIN
      api/uploads/datasets/dataset_45.xlsx
  64. BIN
      api/uploads/datasets/dataset_46.xlsx
  65. BIN
      api/uploads/datasets/dataset_47.xlsx
  66. BIN
      api/uploads/datasets/dataset_48.xlsx
  67. BIN
      api/uploads/datasets/dataset_49.xlsx
  68. BIN
      api/uploads/datasets/dataset_50.xlsx
  69. BIN
      api/uploads/datasets/dataset_51.xlsx
  70. BIN
      api/uploads/datasets/dataset_52.xlsx
  71. BIN
      api/uploads/datasets/dataset_53.xlsx
  72. BIN
      api/uploads/datasets/dataset_54.xlsx
  73. BIN
      api/uploads/datasets/dataset_55.xlsx
  74. BIN
      api/uploads/datasets/dataset_56.xlsx
  75. BIN
      api/uploads/datasets/dataset_57.xlsx
  76. BIN
      api/uploads/datasets/dataset_58.xlsx
  77. BIN
      api/uploads/datasets/dataset_59.xlsx
  78. BIN
      api/uploads/datasets/dataset_6.xlsx
  79. BIN
      api/uploads/datasets/dataset_60.xlsx
  80. BIN
      api/uploads/datasets/dataset_61.xlsx
  81. BIN
      api/uploads/datasets/dataset_62.xlsx
  82. BIN
      api/uploads/datasets/dataset_64.xlsx
  83. BIN
      api/uploads/datasets/dataset_65.xlsx
  84. BIN
      api/uploads/datasets/dataset_7.xlsx
  85. BIN
      api/uploads/f16c82a8-fa7e-4ca7-b172-f71c502d1238.png
  86. BIN
      api/uploads/f50da3f8-a6f1-4313-901f-06ec12c2538b.png
  87. BIN
      assets/taddar/W020241210635035429857_ORIGIN.jpg
  88. BIN
      assets/taddar/首页.png
  89. 48 0
      custom-tab-bar/index.js
  90. 10 0
      custom-tab-bar/index.json
  91. 7 0
      custom-tab-bar/index.wxml
  92. 38 0
      custom-tab-bar/index.wxss
  93. 66 0
      pages/Overview/Overview.js
  94. 6 0
      pages/Overview/Overview.json
  95. 3 0
      pages/Overview/Overview.wxml
  96. 21 0
      pages/Overview/Overview.wxss
  97. 273 0
      pages/Regular/Regular.js
  98. 6 0
      pages/Regular/Regular.json
  99. 44 0
      pages/Regular/Regular.wxml
  100. 206 0
      pages/Regular/Regular.wxss

BIN
api/SoilAcidification.db


+ 442 - 0
api/api_db.py

@@ -0,0 +1,442 @@
+import os
+import sqlite3
+from flask import Flask, jsonify, request, send_file, g
+from werkzeug.utils import secure_filename
+from flask_cors import CORS
+import pandas as pd
+from io import BytesIO
+import logging
+
+app = Flask(__name__)
+
+# 设置数据库文件和上传文件夹的路径
+DATABASE = 'SoilAcidification.db'
+UPLOAD_FOLDER = 'uploads'
+app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
+os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
+
+# 跨源资源共享(CORS)配置,允许跨域请求
+CORS(app)
+
+# 设置日志
+logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+# 创建一个数据库连接池
+def get_db():
+    db = getattr(g, '_database', None)
+    if db is None:
+        db = g._database = sqlite3.connect(DATABASE)
+    return db
+
+# 获取列名的函数
+def get_column_names(table_name):
+    db = get_db()
+    cur = db.cursor()
+    cur.execute(f"PRAGMA table_info({table_name});")
+    columns = [column[1] for column in cur.fetchall()]
+    db.close()
+    return columns
+
+@app.after_request
+def add_headers(response):
+    response.headers['Content-Type'] = 'application/json; charset=utf-8'
+    return response
+
+@app.route('/')
+def index():
+    return 'Hello, World!'
+
+# 有这个函数来获取列名
+def get_column_names(table_name):
+    """
+    根据表名获取对应的列名(字段)。
+    """
+    if table_name == 'current_reflux':
+        # 返回列名映射
+        return ['OM', 'CL', 'CEC', 'H_plus', 'HN', 'Al3_plus', 'Free_alumina', 'Free_iron_oxides', 'Delta_pH'], \
+               ["有机质含量", "土壤粘粒重量", "阳离子交换量", "氢离子含量", "硝态氮含量", "铝离子含量", "游离氧化铝", "游离氧化铁", "酸碱差值"]
+    elif table_name == 'current_reduce':
+        # 返回列名映射
+        return ['Q_over_b', 'pH', 'OM', 'CL', 'H', 'Al'], \
+               ["比值", "酸碱度", "有机质含量", "氯离子含量", "氢离子含量", "铝离子含量"]
+    else:
+        return [], []  # 不支持的表名,返回空列表
+
+# 模板下载接口
+@app.route('/download_template', methods=['GET'])
+def download_template():
+        """
+        根据表名生成模板文件(Excel 或 CSV),并返回下载。
+        :return: 返回模板文件
+        """
+        table_name = request.args.get('table')  # 从请求参数中获取表名
+        if not table_name:
+            return jsonify({'error': '表名参数缺失'}), 400
+
+        # 获取表的列名
+        try:
+            column_names = get_column_names(table_name)
+        except sqlite3.Error as e:
+            return jsonify({'error': f'获取列名失败: {str(e)}'}), 400
+
+        if not column_names:
+            return jsonify({'error': f'未找到表 {table_name} 或列名为空'}), 400
+
+        # 根据表名映射列名(可自定义标题)
+        if table_name == 'current_reflux':
+            column_titles = ["有机质含量", "土壤粘粒重量", "阳离子交换量", "氢离子含量", "硝态氮含量", "铝离子含量",
+                             "游离氧化铝", "游离氧化铁", "酸碱差值"]
+        elif table_name == 'current_reduce':
+            column_titles = ["比值", "酸碱度", "有机质含量", "氯离子含量", "氢离子含量1", "铝离子含量1"]
+        else:
+            return jsonify({'error': f'不支持的表名 {table_name}'}), 400
+
+        # 使用 pandas 创建一个空的 DataFrame,列名为自定义的标题
+        df = pd.DataFrame(columns=column_titles)
+
+        # 根据需求选择模板格式:Excel 或 CSV
+        file_format = request.args.get('format', 'excel').lower()
+        if file_format == 'csv':
+            # 将 DataFrame 写入内存中的 CSV 文件
+            output = BytesIO()
+            df.to_csv(output, index=False, encoding='utf-8')
+            output.seek(0)  # 返回文件开头
+            return send_file(output, as_attachment=True, download_name=f'{table_name}_template.csv',
+                             mimetype='text/csv')
+
+        else:
+            # 默认生成 Excel 文件
+            output = BytesIO()
+            df.to_excel(output, index=False, engine='openpyxl')
+            output.seek(0)  # 返回文件开头
+            return send_file(output, as_attachment=True, download_name=f'{table_name}_template.xlsx',
+                             mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+
+# 模板导入接口
+@app.route('/import_data', methods=['POST'])
+def import_data():
+    """
+    导入数据接口,根据表名将 Excel 或 CSV 数据导入到对应的数据库表中。
+    """
+    # 检查是否有文件
+    if 'file' not in request.files:
+        logging.error("请求中没有文件")
+        return jsonify({'success': False, 'message': '文件缺失'}), 400
+
+    file = request.files['file']
+    table_name = request.form.get('table')
+
+    # 检查表名参数
+    if not table_name:
+        logging.error("缺少表名参数")
+        return jsonify({'success': False, 'message': '缺少表名参数'}), 400
+
+    # 检查文件是否为空
+    if file.filename == '':
+        logging.error("文件为空")
+        return jsonify({'success': False, 'message': '未选择文件'}), 400
+
+    # 获取表的字段映射
+    db_columns, display_titles = get_column_names(table_name)
+
+    # 校验是否支持该表名
+    if not db_columns:
+        logging.error(f"不支持的表名: {table_name}")
+        return jsonify({'success': False, 'message': f'不支持的表名: {table_name}'}), 400
+
+    logging.info(f"表名 {table_name} 对应的字段: {db_columns}")
+
+    # 中文列名到数据库列名的映射
+    column_name_mapping = {
+        "有机质含量": "OM",
+        "土壤粘粒重量": "CL",
+        "阳离子交换量": "CEC",
+        "氢离子含量": "H_plus",
+        "硝态氮含量": "HN",
+        "铝离子含量": "Al3_plus",
+        "游离氧化铝": "Free_alumina",
+        "游离氧化铁": "Free_iron_oxides",
+        "酸碱差值": "Delta_pH",
+        "比值": "Q_over_b",
+        "酸碱度": "pH",
+        "氯离子含量": "CL",
+        "氢离子含量1": "H",
+        "铝离子含量1": "Al"
+    }
+
+    try:
+        # 保存上传文件到临时目录
+        temp_path = os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(file.filename))
+        logging.info(f"正在保存文件到临时路径: {temp_path}")
+        file.save(temp_path)
+
+        # 读取文件内容(支持 Excel 和 CSV 格式)
+        if file.filename.endswith('.xlsx'):
+            logging.info(f"读取 Excel 文件: {file.filename}")
+            df = pd.read_excel(temp_path)
+        elif file.filename.endswith('.csv'):
+            logging.info(f"读取 CSV 文件: {file.filename}")
+            df = pd.read_csv(temp_path)
+        else:
+            logging.error("仅支持 Excel 和 CSV 文件")
+            return jsonify({'success': False, 'message': '仅支持 Excel 和 CSV 文件'}), 400
+
+        logging.info(f"文件读取成功,列名: {df.columns.tolist()}")
+
+        # 1. 移除“序号”列(如果存在)
+        if '序号' in df.columns:
+            df = df.drop(columns=['序号'])
+
+        # 2. 将文件中的列名(中文)转换为数据库列名(英文)
+        df.columns = [column_name_mapping.get(col, col) for col in df.columns]
+
+        logging.info(f"转换后的列名: {df.columns.tolist()}")
+
+        # 校验文件的列是否与数据库表字段匹配
+        if not set(db_columns).issubset(set(df.columns)):
+            logging.error(f"文件列名与数据库表不匹配. 文件列: {df.columns.tolist()}, 期望列: {db_columns}")
+            return jsonify({'success': False, 'message': '文件列名与数据库表不匹配'}), 400
+
+        logging.info("开始清洗数据:移除空行并按需要格式化")
+        # 数据清洗:移除空行并按需要格式化
+        df_cleaned = df[db_columns].dropna()
+        logging.info(f"清洗后数据行数: {len(df_cleaned)}")
+
+        # 插入数据到数据库
+        conn = get_db()
+        logging.info(f"开始将数据插入到表 {table_name} 中")
+
+        # 使用事务确保操作的一致性
+        with conn:
+            df_cleaned.to_sql(table_name, conn, if_exists='append', index=False)
+
+        logging.info(f"数据成功插入到表 {table_name} 中,插入行数: {len(df_cleaned)}")
+
+        # 删除临时文件
+        os.remove(temp_path)
+        logging.info(f"临时文件 {temp_path} 删除成功")
+
+        return jsonify({'success': True, 'message': '数据导入成功'}), 200
+
+    except Exception as e:
+        logging.error(f"导入失败: {e}", exc_info=True)  # 捕获堆栈信息,方便调试
+        return jsonify({'success': False, 'message': f'导入失败: {str(e)}'}), 500
+
+
+# 导出数据接口
+@app.route('/export_data', methods=['GET'])
+def export_data():
+    """
+    根据表名从数据库导出数据,并返回 Excel 或 CSV 格式的文件
+    :return: 返回文件
+    """
+    # 获取表名和文件格式
+    table_name = request.args.get('table')
+    if not table_name:
+        return jsonify({'error': '缺少表名参数'}), 400
+
+    file_format = request.args.get('format', 'excel').lower()  # 默认生成 Excel 文件
+
+    # 获取数据
+    conn = get_db()
+    query = f"SELECT * FROM {table_name};"  # 查询表中的所有数据
+    df = pd.read_sql(query, conn)
+    conn.close()
+
+    if file_format == 'csv':
+        # 将数据保存为 CSV 格式
+        output = BytesIO()
+        df.to_csv(output, index=False)
+        output.seek(0)
+        return send_file(output, as_attachment=True, download_name=f'{table_name}_data.csv',
+                         mimetype='text/csv')
+
+    else:
+        # 默认生成 Excel 格式
+        output = BytesIO()
+        df.to_excel(output, index=False, engine='openpyxl')
+        output.seek(0)
+        return send_file(output, as_attachment=True, download_name=f'{table_name}_data.xlsx',
+                         mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+
+# 添加记录
+@app.route('/add_item', methods=['POST'])
+def add_item():
+    db = get_db()
+    try:
+        # 确保请求体是JSON格式
+        data = request.get_json()
+        if not data:
+            raise ValueError("No JSON data provided")
+
+        table_name = data.get('table')
+        item_data = data.get('item')
+
+        if not table_name or not item_data:
+            return jsonify({'error': 'Missing table name or item data'}), 400
+
+        cur = db.cursor()
+
+        # 动态构建 SQL 语句
+        columns = ', '.join(item_data.keys())
+        placeholders = ', '.join(['?'] * len(item_data))
+        sql = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})"
+        cur.execute(sql, tuple(item_data.values()))
+        db.commit()
+
+        return jsonify({'success': True, 'message': 'Item added successfully'}), 201
+
+    except ValueError as e:
+        return jsonify({'error': str(e)}), 400
+    except KeyError as e:
+        return jsonify({'error': f'Missing data field: {e}'}), 400
+    except sqlite3.IntegrityError as e:
+        return jsonify({'error': 'Database integrity error', 'details': str(e)}), 409
+    except sqlite3.Error as e:
+        return jsonify({'error': 'Database error', 'details': str(e)}), 500
+    finally:
+        db.close()
+
+# 删除记录
+@app.route('/delete_item', methods=['POST'])
+def delete_item():
+    data = request.get_json()
+    table_name = data.get('table')
+    condition = data.get('condition')
+
+    # 日志:检查是否收到正确的参数
+    logging.debug(f"Received data: table={table_name}, condition={condition}")
+
+    if not table_name or not condition:
+        logging.error("缺少表名或条件参数")
+        return jsonify({"success": False, "message": "缺少表名或条件参数"}), 400
+
+    try:
+        key, value = condition.split('=')
+    except ValueError:
+        logging.error(f"条件格式错误,received condition: {condition}")
+        return jsonify({"success": False, "message": "条件格式错误,应为 'key=value'"}), 400
+
+    db = get_db()
+    cur = db.cursor()
+
+    # 日志:确认开始删除操作
+    logging.debug(f"Attempting to delete from {table_name} where {key} = {value}")
+
+    try:
+        cur.execute(f"DELETE FROM {table_name} WHERE {key} = ?", (value,))
+        db.commit()
+
+        # 日志:删除成功
+        logging.info(f"Record deleted from {table_name} where {key} = {value}")
+        return jsonify({"success": True, "message": "记录删除成功"}), 200
+    except sqlite3.Error as e:
+        db.rollback()
+        # 日志:捕获删除失败的错误
+        logging.error(f"Failed to delete from {table_name} where {key} = {value}. Error: {e}")
+        return jsonify({"success": False, "message": f"删除失败: {e}"}), 400
+
+# 更新记录
+@app.route('/update_item', methods=['PUT'])
+def update_record():
+    data = request.get_json()
+
+    if not data or 'table' not in data or 'item' not in data:
+        return jsonify({"success": False, "message": "请求数据不完整"}), 400
+
+    table_name = data['table']
+    item = data['item']
+
+    if not item or next(iter(item.keys())) is None:
+        return jsonify({"success": False, "message": "记录数据为空"}), 400
+
+    id_key = next(iter(item.keys()))
+    record_id = item[id_key]
+    updates = {key: value for key, value in item.items() if key != id_key}
+
+    db = get_db()
+    cur = db.cursor()
+
+    try:
+        record_id = int(record_id)
+    except ValueError:
+        return jsonify({"success": False, "message": "ID 必须是整数"}), 400
+
+    parameters = list(updates.values()) + [record_id]
+    set_clause = ','.join([f"{k} = ?" for k in updates.keys()])
+    sql = f"UPDATE {table_name} SET {set_clause} WHERE {id_key} = ?"
+
+    try:
+        cur.execute(sql, parameters)
+        db.commit()
+        if cur.rowcount == 0:
+            return jsonify({"success": False, "message": "未找到要更新的记录"}), 404
+        return jsonify({"success": True, "message": "数据更新成功"}), 200
+    except sqlite3.Error as e:
+        db.rollback()
+        return jsonify({"success": False, "message": f"更新失败: {e}"}), 400
+
+# 查询记录
+@app.route('/search/record', methods=['GET'])
+def sql_search():
+    try:
+        data = request.get_json()
+        sql_table = data['table']
+        record_id = data['id']
+
+        db = get_db()
+        cur = db.cursor()
+
+        sql = f"SELECT * FROM {sql_table} WHERE id = ?"
+        cur.execute(sql, (record_id,))
+
+        rows = cur.fetchall()
+        column_names = [desc[0] for desc in cur.description]
+
+        if not rows:
+            return jsonify({'error': '未查找到对应数据。'}), 400
+
+        results = []
+        for row in rows:
+            result = {column_names[i]: row[i] for i in range(len(row))}
+            results.append(result)
+
+        cur.close()
+        db.close()
+
+        return jsonify(results), 200
+
+    except sqlite3.Error as e:
+        return jsonify({'error': str(e)}), 400
+    except KeyError as e:
+        return jsonify({'error': f'缺少必要的数据字段: {e}'}), 400
+
+# 提供表格数据
+@app.route('/tables', methods=['POST'])
+def get_table():
+    data = request.get_json()
+    table_name = data.get('table')
+
+    if not table_name:
+        return jsonify({'error': '需要表名'}), 400
+
+    db = get_db()
+    try:
+        cur = db.cursor()
+        cur.execute(f"SELECT * FROM {table_name}")
+        rows = cur.fetchall()
+
+        if not rows:
+            return jsonify({'error': f'表 {table_name} 为空或不存在'}), 400
+
+        headers = [description[0] for description in cur.description]
+        return jsonify(rows=rows, headers=headers), 200
+    except sqlite3.Error as e:
+        return jsonify({'error': str(e)}), 400
+    finally:
+        db.close()
+
+if __name__ == '__main__':
+    app.run(debug=True)

+ 212 - 137
api/app/routes.py

@@ -1,10 +1,10 @@
 import sqlite3
+import uuid
 from io import BytesIO
-import pickle
 
-from flask import Blueprint, request, jsonify, current_app, send_file
+from flask import Blueprint, request, jsonify, current_app, send_file, session
 from werkzeug.security import check_password_hash, generate_password_hash
-from werkzeug.utils import secure_filename
+from werkzeug.utils import secure_filename, send_from_directory
 
 from .model import predict, train_and_save_model, calculate_model_score
 import pandas as pd
@@ -20,7 +20,6 @@ import logging
 from sqlalchemy import text, select, MetaData, Table, func
 from .tasks import train_model_task
 from datetime import datetime
-from sklearn.metrics import r2_score
 
 # 配置日志
 logging.basicConfig(level=logging.DEBUG)
@@ -389,41 +388,46 @@ def get_model(model_id):
         session.close()
 
 
+# 显示切换模型
 @bp.route('/models', methods=['GET'])
-def get_all_models():
-    """
-    获取所有模型信息的API接口
-    
-    @return: JSON响应
-    """
-    Session = sessionmaker(bind=db.engine)
-    session = Session()
-    
+def get_models():
+    session = None
     try:
+        # 创建 session
+        Session = sessionmaker(bind=db.engine)
+        session = Session()
+
+        # 查询所有模型
         models = session.query(Models).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
-                }
-                for model in models
-            ]
-            return jsonify(result)
-        else:
-            return jsonify({'message': '未找到任何模型'}), 404
-            
+
+        logger.debug(f"Models found: {models}")  # 打印查询的模型数据
+
+        if not models:
+            return jsonify({'message': 'No models found'}), 404
+
+        # 将模型数据转换为字典列表
+        models_list = [
+            {
+                'ModelID': model.ModelID,
+                'ModelName': model.Model_name,
+                'ModelType': model.Model_type,
+                'CreatedAt': model.Created_at.strftime('%Y-%m-%d %H:%M:%S'),
+                'Description': model.Description,
+                'DatasetID': model.DatasetID,
+                'ModelFilePath': model.ModelFilePath,
+                'DataType': model.Data_type,
+                'PerformanceScore': model.Performance_score
+            }
+            for model in models
+        ]
+
+        return jsonify(models_list), 200
+
     except Exception as e:
-        logger.error(f'获取所有模型信息失败: {str(e)}')
-        return jsonify({'error': '服务器内部错误', 'message': str(e)}), 500
-        
+        return jsonify({'error': str(e)}), 400
     finally:
-        session.close()
+        if session:
+            session.close()
 
 
 @bp.route('/model-parameters', methods=['GET'])
@@ -514,7 +518,7 @@ def add_item():
         duplicate_check_rules = {
             'users': ['email', 'username'],
             'products': ['product_code'],
-            'current_reduce': [ 'Q_over_b', 'pH', 'OM', 'CL', 'H', 'Al'],
+            'current_reduce': ['Q_over_b', 'pH', 'OM', 'CL', 'H', 'Al'],
             'current_reflux': ['OM', 'CL', 'CEC', 'H_plus', 'N', 'Al3_plus', 'Delta_pH'],
             # 其他表和规则
         }
@@ -611,6 +615,7 @@ def delete_item():
             "message": f"删除失败: {e}"
         }), 500
 
+
 # 定义修改数据库记录的 API 接口
 @bp.route('/update_item', methods=['PUT'])
 def update_record():
@@ -875,6 +880,7 @@ def delete_model_route(model_id):
     # 调用原始函数
     return delete_model(model_id, delete_dataset=delete_dataset_param)
 
+
 def delete_model(model_id, delete_dataset=False):
     """
     删除指定模型的API接口
@@ -899,19 +905,16 @@ def delete_model(model_id, delete_dataset=False):
         session.commit()
         
         # 2. 删除模型文件
-        model_path = model.ModelFilePath
-        try:
-            if os.path.exists(model_path):
+        model_file = f"rf_model_{model_id}.pkl"
+        model_path = os.path.join(current_app.config['MODEL_SAVE_PATH'], model_file)
+        if os.path.exists(model_path):
+            try:
                 os.remove(model_path)
-            else:
+            except OSError as e:
                 # 如果删除文件失败,回滚数据库操作
-                session.rollback() 
-                logger.warning(f'模型文件不存在: {model_path}')
-        except OSError as e:
-            # 如果删除文件失败,回滚数据库操作
-            session.rollback() 
-            logger.error(f'删除模型文件失败: {str(e)}')
-            return jsonify({'error': f'删除模型文件失败: {str(e)}'}), 500
+                session.rollback()
+                logger.error(f'删除模型文件失败: {str(e)}')
+                return jsonify({'error': f'删除模型文件失败: {str(e)}'}), 500
 
         # 3. 如果需要删除关联的数据集
         if delete_dataset and dataset_id:
@@ -931,7 +934,7 @@ def delete_model(model_id, delete_dataset=False):
 
         response_data = {
             'message': '模型删除成功',
-            'deleted_files': [model_path]
+            'deleted_files': [model_file]
         }
         
         if delete_dataset:
@@ -991,54 +994,59 @@ def clear_dataset(data_type):
         session.close()
 
 
+# 修改了一下登录·接口
 @bp.route('/login', methods=['POST'])
 def login_user():
-    # 获取前端传来的数据
     data = request.get_json()
-    name = data.get('name')  # 用户名
-    password = data.get('password')  # 密码
+    logger.debug(f"Received login data: {data}")  # 增加调试日志
+    name = data.get('name')
+    password = data.get('password')
 
-    logger.info(f"Login request received: name={name}")
+    logger.info(f"Login request received for user: {name}")
+
+    if not isinstance(name, str) or not isinstance(password, str):
+        logger.warning("Username and password must be strings")
+        return jsonify({"success": False, "message": "用户名和密码必须为字符串"}), 400
 
-    # 检查用户名和密码是否为空
     if not name or not password:
-        logger.warning("用户名和密码不能为空")
+        logger.warning("Username and password cannot be empty")
         return jsonify({"success": False, "message": "用户名和密码不能为空"}), 400
 
-    try:
-        # 查询数据库验证用户名
-        query = "SELECT * FROM users WHERE name = :name"
-        conn = get_db()
-        user = conn.execute(query, {"name": name}).fetchone()
-
-        if not user:
-            logger.warning(f"用户名 '{name}' 不存在")
-            return jsonify({"success": False, "message": "用户名不存在"}), 400
-
-        # 获取数据库中存储的密码(假设密码是哈希存储的)
-        stored_password = user[2]  # 假设密码存储在数据库的第三列
-        user_id = user[0]  # 假设 id 存储在数据库的第一列
+    query = "SELECT id, name, password FROM users WHERE name = ?"
 
-        # 校验密码是否正确
-        if check_password_hash(stored_password, password):
-            logger.info(f"User '{name}' logged in successfully.")
-            return jsonify({
-                "success": True,
-                "message": "登录成功",
-                "userId": user_id  # 返回用户 ID
-            })
-        else:
-            logger.warning(f"Invalid password for user '{name}'")
-            return jsonify({"success": False, "message": "用户名或密码错误"}), 400
+    try:
+        with get_db() as conn:
+            user = conn.execute(query, (name,)).fetchone()
+
+            if not user:
+                logger.warning(f"User '{name}' does not exist")
+                return jsonify({"success": False, "message": "用户名不存在"}), 400
+
+            stored_password = user[2]  # 假设 'password' 是第三个字段
+            user_id = user[0]  # 假设 'id' 是第一个字段
+
+            if check_password_hash(stored_password, password):
+                session['name'] = name
+                logger.info(f"User '{name}' logged in successfully.")
+                return jsonify({
+                    "success": True,
+                    "message": "登录成功",
+                    "userId": user_id,
+                    "name": name
+                })
+            else:
+                logger.warning(f"Incorrect password for user '{name}'")
+                return jsonify({"success": False, "message": "用户名或密码错误"}), 400
 
+    except sqlite3.DatabaseError as db_err:
+        logger.error(f"Database error during login process: {db_err}", exc_info=True)
+        return jsonify({"success": False, "message": "数据库错误"}), 500
     except Exception as e:
-        # 记录错误日志并返回错误信息
-        logger.error(f"Error during login: {e}", exc_info=True)
+        logger.error(f"Unexpected error during login process: {e}", exc_info=True)
         return jsonify({"success": False, "message": "登录失败"}), 500
 
-# 更新用户信息接口
-
 
+# 更新用户信息接口
 @bp.route('/update_user', methods=['POST'])
 def update_user():
     # 获取前端传来的数据
@@ -1339,6 +1347,7 @@ def download_template():
         logger.error(f"Failed to generate template: {e}", exc_info=True)
         return jsonify({'error': '生成模板文件失败'}), 500
 
+
 @bp.route('/update-threshold', methods=['POST'])
 def update_threshold():
     """
@@ -1395,6 +1404,7 @@ def get_threshold():
             'error': f'获取阈值失败: {str(e)}'
         }), 500
 
+
 @bp.route('/set-current-dataset/<string:data_type>/<int:dataset_id>', methods=['POST'])
 def set_current_dataset(data_type, dataset_id):
     """
@@ -1456,6 +1466,7 @@ 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):
     """
@@ -1481,6 +1492,8 @@ def get_model_history(data_type):
                 Models.DatasetID == dataset.Dataset_ID,
                 Models.Model_name.like(f'auto_trained_{data_type}_%')
             ).first()
+            print(model)
+            print(dataset)
             
             if model and model.Performance_score is not None:
                 # 直接使用数据库中的时间,不进行格式化(保持与created_at相同的时区)
@@ -1506,6 +1519,7 @@ def get_model_history(data_type):
             'performance_scores': [item['performance_score'] for item in history_data],
             'model_details': history_data  # 保留完整数据供前端使用
         }
+        print(response_data)
         
         return jsonify(response_data), 200
         
@@ -1516,6 +1530,7 @@ def get_model_history(data_type):
     finally:
         session.close()
 
+
 @bp.route('/batch-delete-datasets', methods=['POST'])
 def batch_delete_datasets():
     """
@@ -1580,6 +1595,7 @@ def batch_delete_datasets():
         logger.error(f'批量删除数据集失败: {str(e)}')
         return jsonify({'error': str(e)}), 500
 
+
 @bp.route('/batch-delete-models', methods=['POST'])
 def batch_delete_models():
     """
@@ -1645,6 +1661,7 @@ def batch_delete_models():
         logger.error(f'批量删除模型失败: {str(e)}')
         return jsonify({'error': str(e)}), 500
 
+
 @bp.route('/kriging_interpolation', methods=['POST'])
 def kriging_interpolation():
     try:
@@ -1667,64 +1684,122 @@ def kriging_interpolation():
     except Exception as e:
         return jsonify({"error": str(e)}), 500
 
-@bp.route('/model-scatter-data/<int:model_id>', methods=['GET'])
-def get_model_scatter_data(model_id):
-    """
-    获取指定模型的散点图数据(真实值vs预测值)
-    
-    @param model_id: 模型ID
-    @return: JSON响应,包含散点图数据
-    """
-    Session = sessionmaker(bind=db.engine)
-    session = Session()
-    
+
+# 切换模型接口
+@bp.route('/switch-model', methods=['POST'])
+def switch_model():
+    session = None
     try:
-        # 查询模型信息
+        data = request.get_json()
+        model_id = data.get('model_id')
+        model_name = data.get('model_name')
+
+        # 创建 session
+        Session = sessionmaker(bind=db.engine)
+        session = Session()
+
+        # 查找模型
         model = session.query(Models).filter_by(ModelID=model_id).first()
         if not model:
-            return jsonify({'error': '未找到指定模型'}), 404
-            
-        # 加载模型
-        with open(model.ModelFilePath, 'rb') as f:
-            ML_model = pickle.load(f)
-            
-        # 根据数据类型加载测试数据
-        if model.Data_type == 'reflux':
-            X_test = pd.read_csv('uploads/data/X_test_reflux.csv')
-            Y_test = pd.read_csv('uploads/data/Y_test_reflux.csv')
-        elif model.Data_type == 'reduce':
-            X_test = pd.read_csv('uploads/data/X_test_reduce.csv')
-            Y_test = pd.read_csv('uploads/data/Y_test_reduce.csv')
-        else:
-            return jsonify({'error': '不支持的数据类型'}), 400
-            
-        # 获取预测值
-        y_pred = ML_model.predict(X_test)
-        
-        # 生成散点图数据
-        scatter_data = [
-            [float(true), float(pred)] 
-            for true, pred in zip(Y_test.iloc[:, 0], y_pred)
-        ]
-        
-        # 计算R²分数
-        r2 = r2_score(Y_test, y_pred)
-        
-        # 获取数据范围,用于绘制对角线
-        y_min = min(min(Y_test.iloc[:, 0]), min(y_pred))
-        y_max = max(max(Y_test.iloc[:, 0]), max(y_pred))
-        
-        return jsonify({
-            'scatter_data': scatter_data,
-            'r2_score': float(r2),
-            'y_range': [float(y_min), float(y_max)],
-            'model_name': model.Model_name,
-            'model_type': model.Model_type
-        }), 200
-        
+            return jsonify({'error': 'Model not found'}), 404
+
+        # 更新模型状态(或其他切换逻辑)
+        # 假设此处是更新模型的某些字段来进行切换
+        model.status = 'active'  # 假设有一个字段记录模型状态
+        session.commit()
+
+        # 记录切换日志
+        logger.info(f'Model {model_name} (ID: {model_id}) switched successfully.')
+
+        return jsonify({'success': True, 'message': f'Model {model_name} switched successfully!'}), 200
+
     except Exception as e:
-        logger.error(f'获取模型散点图数据失败: {str(e)}', exc_info=True)
-        return jsonify({'error': f'获取数据失败: {str(e)}'}), 500
-        
+        logger.error('Failed to switch model:', exc_info=True)
+        return jsonify({'error': str(e)}), 400
     finally:
-        session.close()
+        if session:
+            session.close()
+
+
+# 添加查看登录状态接口
+@bp.route('/getInfo', methods=['GET'])
+def getInfo_user():
+    name = session.get("name")
+    if name is not None:
+        return jsonify(name=name)
+    else:
+        return jsonify(msg="错误")
+
+
+# 添加退出登录状态接口
+@bp.route('/logout', methods=['GET', 'POST'])
+def logout_user():
+    try:
+        session.clear()
+        return jsonify({"msg": "退出成功"}), 200
+    except Exception as e:
+        logger.error(f"Error during logout process: {e}", exc_info=True)
+        return jsonify({"msg": "退出失败"}), 500
+
+
+
+# 获取软件介绍信息的路由
+@bp.route('/software-intro/<int:id>', methods=['GET'])
+def get_software_intro(id):
+    try:
+        conn = get_db()
+        cursor = conn.cursor()
+        cursor.execute('SELECT title, intro FROM software_intro WHERE id = ?', (id,))
+        result = cursor.fetchone()
+        conn.close()
+
+        if result:
+            title, intro = result
+            return jsonify({
+                'title': title,
+                'intro': intro
+            })
+        return jsonify({}), 404
+    except sqlite3.Error as e:
+        print(f"数据库错误: {e}")
+        return jsonify({"error": f"数据库错误: {str(e)}"}), 500
+
+
+# 更新软件介绍信息的路由
+@bp.route('/software-intro/<int:id>', methods=['PUT'])
+def update_software_intro(id):
+    try:
+        data = request.get_json()
+        title = data.get('title')
+        intro = data.get('intro')
+
+        conn = get_db()
+        cursor = conn.cursor()
+        cursor.execute('UPDATE software_intro SET title =?, intro =? WHERE id = ?', (title, intro, id))
+        conn.commit()
+        conn.close()
+
+        return jsonify({'message': '软件介绍更新成功'})
+    except sqlite3.Error as e:
+        print(f"数据库错误: {e}")
+        return jsonify({"error": f"数据库错误: {str(e)}"}), 500
+
+
+# 处理图片上传的路由
+@bp.route('/upload-image', methods=['POST'])
+def upload_image():
+    file = request.files['image']
+    if file:
+        filename = str(uuid.uuid4()) + '.' + file.filename.rsplit('.', 1)[1].lower()
+        file.save(os.path.join(bp.config['UPLOAD_FOLDER'], filename))
+        imageUrl = f'http://127.0.0.1:5000/uploads/{filename}'
+        return jsonify({'imageUrl': imageUrl})
+    return jsonify({'error': '未找到图片文件'}), 400
+
+    # 配置静态资源服务
+
+
+@bp.route('/uploads/<path:filename>')
+def serve_image(filename):
+    uploads_folder = os.path.join(bp.root_path, 'uploads')
+    return send_from_directory(uploads_folder, filename)

BIN
api/pkl/rf_model_0104_1145.pkl


BIN
api/pkl/rf_model_0104_1149.pkl


BIN
api/pkl/rf_model_0104_1237.pkl


BIN
api/pkl/rf_model_0104_1259.pkl


BIN
api/pkl/rf_model_0104_1415.pkl


BIN
api/pkl/rf_model_0104_1418.pkl


BIN
api/pkl/rf_model_0104_1420.pkl


BIN
api/pkl/rf_model_0111_2235.pkl


BIN
api/pkl/rf_model_0113_1421.pkl


BIN
api/pkl/rf_model_0118_2214.pkl


BIN
api/pkl/rf_model_0223_1714.pkl


BIN
api/pkl/rf_model_0224_1427.pkl


BIN
api/pkl/rf_model_0224_1510.pkl


BIN
api/pkl/rf_model_0227_1207.pkl


BIN
api/pkl/rf_model_0227_1208.pkl


BIN
api/pkl/rf_model_0227_1448.pkl


BIN
api/pkl/rf_model_0227_1453.pkl


BIN
api/pkl/rf_model_0227_1457.pkl


BIN
api/pkl/rf_model_0227_1501.pkl


BIN
api/pkl/rf_model_0227_1510.pkl


BIN
api/pkl/rf_model_0227_1511.pkl


BIN
api/pkl/rf_model_0227_1512.pkl


BIN
api/pkl/rf_model_0227_1514.pkl


BIN
api/pkl/rf_model_0227_1515.pkl


BIN
api/pkl/rf_model_0227_1521.pkl


BIN
api/pkl/rf_model_0227_1522.pkl


BIN
api/pkl/rf_model_0227_1530.pkl


BIN
api/pkl/rf_model_0227_2348.pkl


BIN
api/pkl/rf_model_0228_1149.pkl


BIN
api/pkl/rf_model_0301_1437.pkl


BIN
api/pkl/rf_model_0301_2213.pkl


BIN
api/pkl/rf_model_0301_2231.pkl


BIN
api/pkl/rf_model_0307_1728.pkl


BIN
api/uploads/02093a92-0220-47fb-8105-44bf04d754df.png


BIN
api/uploads/05da5c85-46d1-43f8-bc7c-7086af62f71f.png


BIN
api/uploads/10c81720-e713-4673-9df3-57e3c60c6eab.png


BIN
api/uploads/1de04420-9a47-4f85-a0bb-c27eaa79c263.png


BIN
api/uploads/43d2f03e-1dd1-4f8e-a813-20b9d184c44b.png


BIN
api/uploads/6e192688-76cc-4694-90e6-875434d258a7.png


BIN
api/uploads/977c4192-b373-4615-93b2-7f0abb4e0694.png


BIN
api/uploads/a32557e1-4e2e-4ae6-9395-1a86258ff91d.png


BIN
api/uploads/bd56314c-9543-42ff-85c1-e1e83e45756b.png


BIN
api/uploads/c49dbcb0-a7c9-4bd6-9e7c-c72659090005.png


BIN
api/uploads/datasets/dataset_29.xlsx


BIN
api/uploads/datasets/dataset_30.xlsx


BIN
api/uploads/datasets/dataset_31.xlsx


BIN
api/uploads/datasets/dataset_32.xlsx


BIN
api/uploads/datasets/dataset_33.xlsx


BIN
api/uploads/datasets/dataset_34.xlsx


BIN
api/uploads/datasets/dataset_35.xlsx


BIN
api/uploads/datasets/dataset_36.xlsx


BIN
api/uploads/datasets/dataset_37.xlsx


BIN
api/uploads/datasets/dataset_38.xlsx


BIN
api/uploads/datasets/dataset_39.xlsx


BIN
api/uploads/datasets/dataset_40.xlsx


BIN
api/uploads/datasets/dataset_41.xlsx


BIN
api/uploads/datasets/dataset_42.xlsx


BIN
api/uploads/datasets/dataset_43.xlsx


BIN
api/uploads/datasets/dataset_44.xlsx


BIN
api/uploads/datasets/dataset_45.xlsx


BIN
api/uploads/datasets/dataset_46.xlsx


BIN
api/uploads/datasets/dataset_47.xlsx


BIN
api/uploads/datasets/dataset_48.xlsx


BIN
api/uploads/datasets/dataset_49.xlsx


BIN
api/uploads/datasets/dataset_50.xlsx


BIN
api/uploads/datasets/dataset_51.xlsx


BIN
api/uploads/datasets/dataset_52.xlsx


BIN
api/uploads/datasets/dataset_53.xlsx


BIN
api/uploads/datasets/dataset_54.xlsx


BIN
api/uploads/datasets/dataset_55.xlsx


BIN
api/uploads/datasets/dataset_56.xlsx


BIN
api/uploads/datasets/dataset_57.xlsx


BIN
api/uploads/datasets/dataset_58.xlsx


BIN
api/uploads/datasets/dataset_59.xlsx


BIN
api/uploads/datasets/dataset_6.xlsx


BIN
api/uploads/datasets/dataset_60.xlsx


BIN
api/uploads/datasets/dataset_61.xlsx


BIN
api/uploads/datasets/dataset_62.xlsx


BIN
api/uploads/datasets/dataset_64.xlsx


BIN
api/uploads/datasets/dataset_65.xlsx


BIN
api/uploads/datasets/dataset_7.xlsx


BIN
api/uploads/f16c82a8-fa7e-4ca7-b172-f71c502d1238.png


BIN
api/uploads/f50da3f8-a6f1-4313-901f-06ec12c2538b.png


BIN
assets/taddar/W020241210635035429857_ORIGIN.jpg


BIN
assets/taddar/首页.png


+ 48 - 0
custom-tab-bar/index.js

@@ -0,0 +1,48 @@
+// custom-tab-bar/index.js
+Component({
+  data: {
+    selected: 0, // 当前选中的 tab 索引
+    color: "#999999",
+    selectedColor: "#F7393F",
+    list: [
+      { 
+        text: "首页", 
+        pagePath: "/pages/Home/Home", 
+        iconPath: "/assets/taddar/首页.png", 
+        selectedIconPath: "/assets/taddar/首页 (1).png" 
+      },
+      { 
+        text: "反酸模型", 
+        pagePath: "/pages/Soil Acidification/Soil Acidification", 
+        iconPath: "/assets/taddar/chart-histogram (1).png", 
+        selectedIconPath: "/assets/taddar/chart-histogram.png" 
+      },
+      { 
+        text: "降酸模型", 
+        pagePath: "/pages/Soil Deacidification/Soil Deacidification", 
+        iconPath: "/assets/taddar/Data_Visualization-active.png", 
+        selectedIconPath: "/assets/taddar/Data_Visualization.png" 
+      },
+      { 
+        text: "人员中心", 
+        pagePath: "/pages/Staff/Staff", 
+        iconPath: "/assets/taddar/users-copy.png", 
+        selectedIconPath: "/assets/taddar/copy.png" 
+      }
+    ]
+  },
+  attached() {
+    // 可在此设置初始选中项
+  },
+  methods: {
+    switchTab(e) {
+      const { index, path } = e.currentTarget.dataset;
+      wx.switchTab({
+        url: path,
+      });
+      this.setData({
+        selected: index, // 更新当前选中的 tab 索引
+      });
+    },
+  },
+});

+ 10 - 0
custom-tab-bar/index.json

@@ -0,0 +1,10 @@
+{
+<<<<<<< HEAD
+  "usingComponents": {},
+  "navigationBarTitleText": "注册",
+  "navigationBarBackgroundColor": "#f8f8f8",  
+  "navigationBarTextStyle": "black"  
+=======
+  "usingComponents": {}
+>>>>>>> origin/soligd
+}

+ 7 - 0
custom-tab-bar/index.wxml

@@ -0,0 +1,7 @@
+<cover-view class="tab-bar">
+  <cover-view class="tab-bar-border"></cover-view>
+  <cover-view wx:for="{{list}}" wx:key="index" class="tab-bar-item" data-path="{{item.pagePath}}" data-index="{{index}}" bindtap="switchTab">
+    <cover-image src="{{selected === index ? item.selectedIconPath : item.iconPath}}"></cover-image>
+    <cover-view style="color: {{selected === index ? selectedColor : color}}">{{item.text}}</cover-view>
+  </cover-view>
+</cover-view>

+ 38 - 0
custom-tab-bar/index.wxss

@@ -0,0 +1,38 @@
+.tab-bar {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  height: 48px;
+  background: white;
+  display: flex;
+  padding-bottom: env(safe-area-inset-bottom);
+}
+ 
+.tab-bar-border {
+  background-color: rgba(0, 0, 0, 0.33);
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 1px;
+  transform: scaleY(0.5);
+}
+ 
+.tab-bar-item {
+  flex: 1;
+  text-align: center;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+}
+ 
+.tab-bar-item cover-image {
+  width: 27px;
+  height: 27px;
+}
+ 
+.tab-bar-item cover-view {
+  font-size: 10px;
+}

+ 66 - 0
pages/Overview/Overview.js

@@ -0,0 +1,66 @@
+// pages/Regular User/Overview/Overview.js
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad(options) {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload() {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh() {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom() {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage() {
+
+  }
+})

+ 6 - 0
pages/Overview/Overview.json

@@ -0,0 +1,6 @@
+{
+  "usingComponents": {},
+  "navigationBarTitleText": "项目简介",
+  "navigationBarBackgroundColor": "#ffffff",  
+  "navigationBarTextStyle": "black"  
+}

+ 3 - 0
pages/Overview/Overview.wxml

@@ -0,0 +1,3 @@
+<view class="green-box">
+  <text class="title-en">项目简介</text>
+</view>

+ 21 - 0
pages/Overview/Overview.wxss

@@ -0,0 +1,21 @@
+/* 页面整体背景 */
+.green-box {
+  background-color: #4caf50; /* 绿色背景 */
+  padding: 10px 20px;  /* 减小上下内边距,控制高度 */
+  text-align: center;
+  display: flex;
+  flex-direction: column; /* 设置垂直方向布局 */
+  justify-content: center;
+  align-items: center;
+  max-height: 100rpx; /* 限制最大高度 */
+  max-width: calc(100% - 10px); /* 宽度最大100%,两边各留5px */
+  margin-left: 15px;  /* 左边留5px */
+  margin-right: 15px; /* 右边留5px */
+  box-sizing: border-box; /* 确保 padding 和 margin 不影响宽度计算 */
+}
+
+.title-en {
+  font-size: 16px;
+  color: #fff;
+  display: block;
+}

+ 273 - 0
pages/Regular/Regular.js

@@ -0,0 +1,273 @@
+Page({
+  data: {
+<<<<<<< HEAD
+    name: '',     // 用户名
+    password: '', // 密码
+    errorMessage: '', // 错误提示
+  },
+
+  // 输入用户名
+  inputName: function (e) {
+    this.setData({
+      name: e.detail.value
+    });
+  },
+
+<<<<<<< HEAD:pages/b/b.js
+  // 输入密码
+  inputPassword: function (e) {
+=======
+=======
+    isLogin: false, // 登录状态
+    isHidden: true, // 弹窗状态
+    avatarUrl: '', // 用户头像
+    nickName: '', // 用户昵称
+    username: '', // 用户名(用户名密码登录)
+    password: '', // 密码(用户名密码登录)
+    errorMessage: '', // 错误信息
+  },
+
+  // 获取用户头像
+  getAvatar(e) {
+    const avatarUrl = e.detail.avatarUrl; // 从事件中获取头像URL
+
+    if (avatarUrl) {
+      this.setData({
+        avatarUrl: avatarUrl
+      });
+
+      // 授权登录后,自动设置默认密码到缓存
+      wx.setStorageSync('password', '123');
+
+      // 创建userInfo对象,保存头像和昵称到缓存
+      let userInfo = {
+        avatarUrl: avatarUrl,
+        nickName: this.data.nickName
+      };
+      wx.setStorageSync('userinfo', userInfo); // 保存到缓存
+    } else {
+      wx.showToast({
+        title: '头像获取失败',
+        icon: 'error',
+        duration: 2000
+      });
+    }
+  },
+
+  // 获取用户昵称
+  getName(e) {
+    const nickName = e.detail.value; // 获取昵称
+    this.setData({
+      nickName: nickName
+    });
+
+    // 在授权登录时,将昵称更新到缓存的 userInfo 对象中
+    let userInfo = wx.getStorageSync('userinfo') || {};
+    userInfo.nickName = nickName;
+    wx.setStorageSync('userinfo', userInfo); // 更新缓存
+  },
+
+  // 显示登录弹窗
+  gologin() {
+    this.setData({
+      isHidden: false
+    });
+  },
+
+>>>>>>> origin/soligd
+  // 取消登录弹窗
+  potNo() {
+    this.setData({
+      isHidden: true
+    });
+  },
+
+  // 确认登录弹窗
+  popYes() {
+    const { avatarUrl, nickName } = this.data;
+    if (!avatarUrl || !nickName) {
+      wx.showToast({
+        icon: 'error',
+        title: '请获取头像和昵称',
+      });
+      return;
+    }
+
+    this.setData({
+      isLogin: true,
+      isHidden: true,
+    });
+
+    // 登录成功后跳转到指定页面
+    wx.switchTab({
+      url: '/pages/Regular User/Home/Home',
+    });
+  },
+
+  // 用户名密码登录
+  inputUsername(e) {
+    this.setData({
+      username: e.detail.value
+    });
+  },
+
+  inputPassword(e) {
+<<<<<<< HEAD
+>>>>>>> origin/soligd:pages/Regular/Regular.js
+=======
+>>>>>>> origin/soligd
+    this.setData({
+      password: e.detail.value
+    });
+  },
+
+<<<<<<< HEAD
+  // 注册逻辑
+  register: function () {
+    const { name, password } = this.data;
+
+    // 检查用户名和密码是否为空
+    if (!name || !password) {
+      wx.showToast({
+        title: '用户名和密码不能为空',
+        icon: 'none',
+        duration: 2000
+=======
+  // 登录验证
+  login() {
+    const { username, password } = this.data;
+
+    if (!username || !password) {
+      this.setData({
+        errorMessage: '用户名和密码不能为空'
+>>>>>>> origin/soligd
+      });
+      return;
+    }
+
+    wx.showLoading({
+<<<<<<< HEAD
+      title: '注册中...'
+    });
+
+    setTimeout(() => {
+      // 发送请求到后端,进行用户注册
+      wx.request({
+        url: 'https://soilgd.com:5000/register', // 后端注册接口
+        method: 'POST',
+        data: {
+          name: name,        // 修改为 name
+          password: password,
+        },
+        success: (res) => {
+          wx.hideLoading();
+          if (res.data.success) {
+            wx.showToast({
+              title: '注册成功',
+              icon: 'success',
+              duration: 2000
+            });
+
+            // 清空输入框
+            this.setData({
+              name: '',
+              password: '',
+              errorMessage: ''
+            });
+
+<<<<<<< HEAD:pages/b/b.js
+            // 跳转到登录页面
+            wx.switchTab({
+              url: '/pages/Home/Home' 
+            });
+          } else {
+            wx.showToast({
+              title: res.data.message || '注册失败',
+              icon: 'none',
+              duration: 2000
+            });
+          }
+        },
+        fail: (err) => {
+          wx.hideLoading();
+          wx.showToast({
+            title: '网络错误,请重试',
+            icon: 'none',
+            duration: 2000
+          });
+          console.error('注册失败:', err);
+        }
+      });
+=======
+=======
+      title: '登录中...'
+    });
+
+    setTimeout(() => {
+      wx.hideLoading();
+
+      // 假设用户名和密码为123时登录成功
+      if (username === '123' && password === '123') {
+        wx.showToast({
+          title: '登录成功',
+          icon: 'success',
+          duration: 2000
+        });
+        this.setData({
+          isLogin: true,
+          errorMessage: '',
+        });
+
+>>>>>>> origin/soligd
+        // 登录成功后跳转到指定页面
+        wx.switchTab({
+          url: '/pages/Regular User/Home/Home',
+        });
+      } else {
+        this.setData({
+          errorMessage: '用户名或密码错误'
+        });
+        wx.showToast({
+          title: '用户名或密码错误',
+          icon: 'none',
+          duration: 2000
+        });
+      }
+<<<<<<< HEAD
+>>>>>>> origin/soligd:pages/Regular/Regular.js
+    }, 1500);
+  },
+=======
+    }, 1500);
+  },
+
+  // 页面加载时,检查是否有缓存的密码
+  onLoad() {
+    const storedPassword = wx.getStorageSync('password');
+    const storedUserInfo = wx.getStorageSync('userinfo');
+
+    // 如果有缓存的密码,则自动填充
+    if (storedPassword) {
+      this.setData({
+        password: storedPassword // 自动填充缓存的密码
+      });
+    }
+
+    // 如果有缓存的用户信息,自动填充头像和昵称
+    if (storedUserInfo) {
+      this.setData({
+        avatarUrl: storedUserInfo.avatarUrl || '/assets/taddar/授权管理.png', // 默认头像
+        nickName: storedUserInfo.nickName || '未登录', // 默认昵称
+        isLogin: true, // 设置已登录状态
+      });
+    }
+
+    // 如果没有设置头像,使用默认头像
+    if (!this.data.avatarUrl) {
+      this.setData({
+        avatarUrl: '/assets/taddar/授权管理.png' // 默认头像
+      });
+    }
+  }
+>>>>>>> origin/soligd
+});

+ 6 - 0
pages/Regular/Regular.json

@@ -0,0 +1,6 @@
+{
+  "usingComponents": {},
+  "navigationBarTitleText": "普通用户登录",
+  "navigationBarBackgroundColor": "#f8f8f8",  
+  "navigationBarTextStyle": "black"  
+}

+ 44 - 0
pages/Regular/Regular.wxml

@@ -0,0 +1,44 @@
+<view class="container">
+<<<<<<< HEAD
+  <input class="input" bindinput="inputName" placeholder="请输入用户名" />
+  <input class="input" bindinput="inputPassword" placeholder="请输入密码" type="password" />
+  <button class="btn" bindtap="register">注册</button>
+  <text class="error">{{errorMessage}}</text>
+=======
+   <!-- 用户名密码登录 -->
+   <view >
+    <input class="input" bindinput="inputUsername" placeholder="请输入用户名" />
+    <input class="input" bindinput="inputPassword" placeholder="请输入密码" type="password" />
+    <button class="gologin" bindtap="login">登录</button>
+    <text class="error">{{errorMessage}}</text>
+  </view>
+  <view class="container">
+<button bindtap="gologin">微信登录</button>
+</view>
+
+  <!-- 登录弹窗 -->
+  <view class="pop_root" hidden="{{isHidden}}">
+    <view class="pop_content">
+      <view class="pot_tip">授权登录弹窗</view>
+      
+      <!-- 获取头像 -->
+      <button class="pop_avatar" open-type="chooseAvatar" bindchooseavatar="getAvatar">
+        <image class="pop_img" wx:if="{{avatarUrl}}" src="{{avatarUrl}}" />
+        <view wx:else>获取头像</view>
+      </button>
+      
+      <!-- 获取昵称 -->
+      <input 
+    class="pop_name" 
+    type="nickname" 
+    bindblur="getName"
+    placeholder="获取昵称"/>
+      
+      <view class="pop_bot">
+        <view class="btn" bind:tap="potNo">取消</view>
+        <view class="btn1" bind:tap="popYes">确定</view>
+      </view>
+    </view>
+  </view>
+>>>>>>> origin/soligd
+</view>

+ 206 - 0
pages/Regular/Regular.wxss

@@ -0,0 +1,206 @@
+.container {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+<<<<<<< HEAD
+  padding: 50px 40px;
+=======
+  padding: 20rpx 40rpx;
+>>>>>>> origin/soligd
+  height: 50vh; /* 使容器占据整个屏幕高度 */
+}
+
+.input {
+  width: 100%;
+<<<<<<< HEAD
+  height: 30px;
+  margin-bottom: 10px;
+  padding: 0 20px;
+  border: 1px solid #ddd;
+  border-radius: var(--border-radius, 10px);
+=======
+  height: 80rpx;
+  margin-bottom: 20rpx;
+  padding: 0 20rpx;
+  border: 1px solid #ddd;
+  border-radius: var(--border-radius, 10rpx);
+>>>>>>> origin/soligd
+  box-sizing: border-box;
+}
+
+.btn {
+<<<<<<< HEAD
+  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%;
+  height: 80rpx;
+  background-color: var(--primary-color, #1aad19);
+  color: #fff;
+  border: none;
+  border-radius: var(--border-radius, 10rpx);
+  text-align: center;
+  line-height: 45rpx;
+  font-size: 36rpx;
+  box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.1);  /* 添加阴影 */
+  transition: background-color 0.3s ease, box-shadow 0.3s ease;  /* 背景颜色和阴影过渡效果 */
+>>>>>>> origin/soligd
+}
+
+.btn:active {
+  background-color: var(--active-color, #128c13);
+  box-shadow: 0 2rpx 5rpx rgba(0, 0, 0, 0.2);  /* 按钮点击时阴影变化 */
+}
+
+.btn:hover {
+  background-color: var(--hover-color, #16b818);
+  cursor: pointer;  /* 鼠标悬浮时显示手型 */
+<<<<<<< HEAD
+=======
+}
+
+.error {
+  color: red;
+  margin-top: 10rpx;
+  animation: fadeIn 0.5s ease;
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+}
+
+.avatar {
+  width: 150rpx;
+  height: 150rpx;
+  border-radius: 50%;
+  margin-top: 20rpx;
+  object-fit: cover;
+}
+
+@media screen and (max-width: 375px) {
+  .container {
+    padding: 100rpx 20rpx;
+  }
+  .btn {
+    font-size: 28rpx;
+    height: 60rpx;
+    line-height: 60rpx;
+  }
+  .input {
+    height: 60rpx;
+  }
+}
+
+/* pages/demo/demo.wxss */
+.root {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+}
+
+.touxiang {
+  width: 300rpx;
+  height: 300rpx;
+  border-radius: 50%;
+  margin-top: 30rpx;
+}
+
+.name {
+  margin-top: 30rpx;
+}
+
+/* 弹窗 */
+.pop_root {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  background: rgb(0, 0, 0, 0.6);
+  z-index: 1000;
+}
+
+.pop_content {
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  background: white;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  border-radius: 30rpx 30rpx 0 0;
+}
+
+.pot_tip {
+  font-size: 48rpx;
+  margin-top: 30rpx;
+}
+
+.pop_avatar {
+  width: 200rpx;
+  height: 200rpx;
+  border-radius: 50%;
+  background: gainsboro;
+  font-size: 35rpx;
+  display: flex;
+  flex-direction: center;
+  align-items: center;
+  margin: 30rpx;
+  padding: 0;
+}
+
+.pop_img {
+  width: 100%;
+  height: 100%;
+}
+
+.pop_name {
+  width: 300rpx;
+  bottom: 1px solid gray;
+  border-radius: 25epx;
+  padding-left: 160rpx;
+  margin-bottom: 50rpx;
+}
+
+.pop_bot {
+  display: flex;
+  margin-bottom: 50rpx;
+
+}
+
+.btn {
+  width: 150rpx;
+  border: 1px solid gray;
+  border-radius: 20rpx;
+  text-align: center;
+  background: red;
+  color: white;
+}
+
+.btn1 {
+  width: 150rpx;
+  border: 1px solid gray;
+  border-radius: 20rpx;
+  text-align: center;
+  background: green;
+  color: white;
+  margin-left: 90rpx;
+>>>>>>> origin/soligd
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott