diff --git a/ESConnect.py b/ESConnect.py index d1abfc4..470f354 100644 --- a/ESConnect.py +++ b/ESConnect.py @@ -3,6 +3,7 @@ from elasticsearch import Elasticsearch # import json import hashlib import requests +import json # Elasticsearch连接配置 ES_URL = "http://localhost:9200" @@ -60,6 +61,9 @@ def create_index_with_mapping(): write_user_data(admin) else: print(f"索引 {users_index_name} 已存在") +def update_document(es, index_name, doc_id=None, updated_doc=None): + """更新指定ID的文档""" + es.update(index=index_name, id=doc_id, body={"doc": updated_doc}) def get_doc_id(data): @@ -142,6 +146,49 @@ def delete_by_id(doc_id): print("删除失败:", str(e)) return False +def update_by_id(doc_id, updated_data): + """ + 根据文档ID更新数据 + + 参数: + doc_id (str): 要更新的文档ID + updated_data (dict): 更新的数据内容 + + 返回: + bool: 更新成功返回True,失败返回False + """ + try: + # 执行更新操作 + es.update(index=index_name, id=doc_id, body={"doc": updated_data}) + print(f"文档 {doc_id} 更新成功") + return True + except Exception as e: + print(f"更新失败: {str(e)}") + return False + +def get_by_id(doc_id): + """ + 根据文档ID获取单个文档 + + 参数: + doc_id (str): 要获取的文档ID + + 返回: + dict or None: 成功返回文档数据,失败返回None + """ + try: + # 执行获取操作 + result = es.get(index=index_name, id=doc_id) + if result['found']: + return { + "_id": result['_id'], + **result['_source'] + } + return None + except Exception as e: + print(f"获取文档失败: {str(e)}") + return None + def search_by_any_field(keyword): """全字段模糊搜索(支持拼写错误)""" try: @@ -216,11 +263,11 @@ def write_user_data(data): def verify_user(username, password): """ 验证用户登录信息 - + 参数: username (str): 用户名 password (str): 密码 - + 返回: dict or None: 验证成功返回用户信息,失败返回None """ @@ -239,7 +286,7 @@ def verify_user(username, password): ) response.raise_for_status() results = response.json()["hits"]["hits"] - + if results: user_data = results[0]["_source"] # 验证密码 @@ -252,7 +299,7 @@ def verify_user(username, password): else: print(f"用户 {username} 不存在") return None - + except requests.exceptions.HTTPError as e: print(f"用户验证失败: {e.response.text}") return None @@ -260,10 +307,10 @@ def verify_user(username, password): def get_user_by_username(username): """ 根据用户名查询用户信息 - + 参数: username (str): 用户名 - + 返回: dict or None: 查询成功返回用户信息,失败返回None """ @@ -281,12 +328,12 @@ def get_user_by_username(username): ) response.raise_for_status() results = response.json()["hits"]["hits"] - + if results: return results[0]["_source"] else: return None - + except requests.exceptions.HTTPError as e: print(f"用户查询失败: {e.response.text}") return None @@ -294,12 +341,12 @@ def get_user_by_username(username): def create_user(username, password, permission=1): """ 创建新用户 - + 参数: username (str): 用户名 password (str): 密码 permission (int): 权限级别,默认为1(普通用户) - + 返回: bool: 创建成功返回True,失败返回False """ @@ -307,24 +354,24 @@ def create_user(username, password, permission=1): if get_user_by_username(username): print(f"用户名 {username} 已存在") return False - + # 生成新的用户ID import time user_id = int(time.time() * 1000) # 使用时间戳作为用户ID - + user_data = { "user_id": user_id, "username": username, "password": password, "premission": permission } - + return write_user_data(user_data) def get_all_users(): """ 获取所有用户信息 - + 返回: list: 包含所有用户信息的列表 """ @@ -341,15 +388,15 @@ def get_all_users(): ) response.raise_for_status() results = response.json()["hits"]["hits"] - + users = [] for hit in results: user_data = hit["_source"] user_data["_id"] = hit["_id"] # 添加文档ID用于后续操作 users.append(user_data) - + return users - + except requests.exceptions.HTTPError as e: print(f"获取用户列表失败: {e.response.text}") return [] @@ -357,11 +404,11 @@ def get_all_users(): def update_user_password(username, new_password): """ 更新用户密码 - + 参数: username (str): 用户名 new_password (str): 新密码 - + 返回: bool: 更新成功返回True,失败返回False """ @@ -380,18 +427,18 @@ def update_user_password(username, new_password): ) response.raise_for_status() results = response.json()["hits"]["hits"] - + if not results: print(f"用户 {username} 不存在") return False - + # 获取用户文档ID doc_id = results[0]["_id"] user_data = results[0]["_source"] - + # 更新密码 user_data["password"] = new_password - + # 更新文档 update_response = requests.post( f"{ES_URL}/{users_index_name}/_doc/{doc_id}", @@ -400,10 +447,10 @@ def update_user_password(username, new_password): headers={"Content-Type": "application/json"} ) update_response.raise_for_status() - + print(f"用户 {username} 密码更新成功") return True - + except requests.exceptions.HTTPError as e: print(f"更新用户密码失败: {e.response.text}") return False @@ -411,10 +458,10 @@ def update_user_password(username, new_password): def delete_user(username): """ 删除用户 - + 参数: username (str): 要删除的用户名 - + 返回: bool: 删除成功返回True,失败返回False """ @@ -423,7 +470,7 @@ def delete_user(username): if username == "admin": print("不能删除管理员账户") return False - + # 先查找用户 response = requests.post( f"{ES_URL}/{users_index_name}/_search", @@ -438,24 +485,24 @@ def delete_user(username): ) response.raise_for_status() results = response.json()["hits"]["hits"] - + if not results: print(f"用户 {username} 不存在") return False - + # 获取用户文档ID doc_id = results[0]["_id"] - + # 删除用户 delete_response = requests.delete( f"{ES_URL}/{users_index_name}/_doc/{doc_id}", auth=AUTH ) delete_response.raise_for_status() - + print(f"用户 {username} 删除成功") return True - + except requests.exceptions.HTTPError as e: print(f"删除用户失败: {e.response.text}") return False @@ -463,11 +510,11 @@ def delete_user(username): def update_user_permission(username, new_permission): """ 更新用户权限 - + 参数: username (str): 用户名 new_permission (int): 新权限级别 - + 返回: bool: 更新成功返回True,失败返回False """ @@ -476,7 +523,7 @@ def update_user_permission(username, new_permission): if username == "admin": print("不能修改管理员权限") return False - + # 先查找用户 response = requests.post( f"{ES_URL}/{users_index_name}/_search", @@ -491,18 +538,18 @@ def update_user_permission(username, new_permission): ) response.raise_for_status() results = response.json()["hits"]["hits"] - + if not results: print(f"用户 {username} 不存在") return False - + # 获取用户文档ID doc_id = results[0]["_id"] user_data = results[0]["_source"] - + # 更新权限 user_data["premission"] = new_permission - + # 更新文档 update_response = requests.post( f"{ES_URL}/{users_index_name}/_doc/{doc_id}", @@ -511,10 +558,10 @@ def update_user_permission(username, new_permission): headers={"Content-Type": "application/json"} ) update_response.raise_for_status() - + print(f"用户 {username} 权限更新成功") return True - + except requests.exceptions.HTTPError as e: print(f"更新用户权限失败: {e.response.text}") return False @@ -522,11 +569,11 @@ def update_user_permission(username, new_permission): def search_data_by_user(user_id, keyword=None): """ 根据用户ID查询该用户的数据,支持关键词搜索 - + 参数: user_id (str): 用户ID keyword (str, optional): 搜索关键词 - + 返回: list: 包含文档ID和源数据的列表 """ @@ -552,7 +599,7 @@ def search_data_by_user(user_id, keyword=None): query = { "term": {"user_id": user_id} } - + response = requests.post( f"{ES_URL}/{data_index_name}/_search", auth=AUTH, @@ -563,13 +610,13 @@ def search_data_by_user(user_id, keyword=None): ) response.raise_for_status() results = response.json()["hits"]["hits"] - + # 返回包含文档ID和源数据的列表 return [{ "_id": hit["_id"], **hit["_source"] } for hit in results] - + except requests.exceptions.HTTPError as e: print(f"查询用户数据失败: {e.response.text}") return [] @@ -577,12 +624,12 @@ def search_data_by_user(user_id, keyword=None): def update_data_by_id(doc_id, updated_data, user_id): """ 根据文档ID更新数据(仅允许数据所有者修改) - + 参数: doc_id (str): 文档ID updated_data (dict): 更新的数据 user_id (str): 当前用户ID - + 返回: bool: 更新成功返回True,失败返回False """ @@ -594,20 +641,20 @@ def update_data_by_id(doc_id, updated_data, user_id): ) response.raise_for_status() doc = response.json() - + # 检查文档是否存在 if not doc.get("found"): print(f"文档 {doc_id} 不存在") return False - + # 检查用户权限(只能修改自己的数据) if doc["_source"].get("user_id") != user_id: print(f"用户 {user_id} 无权修改文档 {doc_id}") return False - + # 保持用户ID不变 updated_data["user_id"] = user_id - + # 更新文档 update_response = requests.post( f"{ES_URL}/{data_index_name}/_doc/{doc_id}", @@ -616,10 +663,10 @@ def update_data_by_id(doc_id, updated_data, user_id): headers={"Content-Type": "application/json"} ) update_response.raise_for_status() - + print(f"文档 {doc_id} 更新成功") return True - + except requests.exceptions.HTTPError as e: print(f"更新文档失败: {e.response.text}") return False @@ -627,11 +674,11 @@ def update_data_by_id(doc_id, updated_data, user_id): def delete_data_by_id(doc_id, user_id): """ 根据文档ID删除数据(仅允许数据所有者或管理员删除) - + 参数: doc_id (str): 文档ID user_id (str): 当前用户ID - + 返回: bool: 删除成功返回True,失败返回False """ @@ -643,12 +690,12 @@ def delete_data_by_id(doc_id, user_id): ) response.raise_for_status() doc = response.json() - + # 检查文档是否存在 if not doc.get("found"): print(f"文档 {doc_id} 不存在") return False - + # 检查用户权限(只能删除自己的数据,管理员可以删除所有数据) doc_user_id = doc["_source"].get("user_id") if doc_user_id != user_id: @@ -657,17 +704,17 @@ def delete_data_by_id(doc_id, user_id): if not user_info or user_info.get("premission") != 0: print(f"用户 {user_id} 无权删除文档 {doc_id}") return False - + # 删除文档 delete_response = requests.delete( f"{ES_URL}/{data_index_name}/_doc/{doc_id}", auth=AUTH ) delete_response.raise_for_status() - + print(f"文档 {doc_id} 删除成功") return True - + except requests.exceptions.HTTPError as e: print(f"删除文档失败: {e.response.text}") return False @@ -675,12 +722,12 @@ def delete_data_by_id(doc_id, user_id): def update_user_own_password(user_id, old_password, new_password): """ 用户修改自己的密码 - + 参数: user_id (str): 用户ID old_password (str): 旧密码 new_password (str): 新密码 - + 返回: bool: 修改成功返回True,失败返回False """ @@ -699,22 +746,22 @@ def update_user_own_password(user_id, old_password, new_password): ) response.raise_for_status() results = response.json()["hits"]["hits"] - + if not results: print(f"用户 {user_id} 不存在") return False - + user_data = results[0]["_source"] doc_id = results[0]["_id"] - + # 验证旧密码 if user_data.get("password") != old_password: print("旧密码错误") return False - + # 更新密码 user_data["password"] = new_password - + # 更新文档 update_response = requests.post( f"{ES_URL}/{users_index_name}/_doc/{doc_id}", @@ -723,10 +770,10 @@ def update_user_own_password(user_id, old_password, new_password): headers={"Content-Type": "application/json"} ) update_response.raise_for_status() - + print(f"用户 {user_id} 密码修改成功") return True - + except requests.exceptions.HTTPError as e: print(f"修改密码失败: {e.response.text}") return False diff --git a/app.py b/app.py index c8c66db..d72d18d 100644 --- a/app.py +++ b/app.py @@ -1,62 +1,19 @@ import base64 -from flask import Flask, request, render_template, redirect, url_for, jsonify, session, flash, send_from_directory -from werkzeug.utils import secure_filename +from flask import Flask, request, render_template, redirect, url_for, jsonify import os import uuid from PIL import Image import re import json -import requests from ESConnect import * from json_converter import json_to_string, string_to_json from openai import OpenAI -from functools import wraps # import config # 创建Flask应用实例 app = Flask(__name__) -# 设置会话密钥,用于加密会话数据 -app.secret_key = 'your-secret-key-change-this-in-production' # app.config.from_object(config.Config) -# 权限装饰器 -def login_required(f): - """要求用户登录的装饰器""" - @wraps(f) - def decorated_function(*args, **kwargs): - if 'user_id' not in session: - flash('请先登录', 'error') - return redirect(url_for('login')) - return f(*args, **kwargs) - return decorated_function - -def admin_required(f): - """要求管理员权限的装饰器""" - @wraps(f) - def decorated_function(*args, **kwargs): - if 'user_id' not in session: - flash('请先登录', 'error') - return redirect(url_for('login')) - if session.get('permission', 1) != 0: - flash('权限不足,需要管理员权限', 'error') - return redirect(url_for('index')) - return f(*args, **kwargs) - return decorated_function - -def user_or_admin_required(f): - """要求普通用户或管理员权限的装饰器""" - @wraps(f) - def decorated_function(*args, **kwargs): - if 'user_id' not in session: - flash('请先登录', 'error') - return redirect(url_for('login')) - permission = session.get('permission', 1) - if permission not in [0, 1]: - flash('权限不足', 'error') - return redirect(url_for('index')) - return f(*args, **kwargs) - return decorated_function - # OCR和信息提取函数,使用大模型API处理图片并提取结构化信息 def ocr_and_extract_info(image_path): """ @@ -181,249 +138,8 @@ def ocr_and_extract_info(image_path): """ -# 登录页面路由 -@app.route('/login', methods=['GET', 'POST']) -def login(): - """ - 处理用户登录 - - GET: 显示登录页面 - POST: 处理登录表单提交 - """ - if request.method == 'POST': - username = request.form.get('username') - password = request.form.get('password') - - if not username or not password: - flash('请输入用户名和密码', 'error') - return render_template('login.html') - - # 验证用户 - user_data = verify_user(username, password) - if user_data: - # 登录成功,设置会话 - session['user_id'] = user_data['user_id'] - session['username'] = user_data['username'] - session['permission'] = user_data['premission'] - flash(f'欢迎回来,{username}!', 'success') - return redirect(url_for('index')) - else: - flash('用户名或密码错误', 'error') - return render_template('login.html') - - return render_template('login.html') - -# 登出路由 -@app.route('/logout') -def logout(): - """ - 处理用户登出 - """ - session.clear() - flash('已成功登出', 'info') - return redirect(url_for('login')) - -# 用户管理页面路由 -@app.route('/user_management') -@admin_required -def user_management(): - """ - 显示用户管理页面(仅管理员可访问) - """ - users = get_all_users() - return render_template('user_management.html', users=users) - -# 注册新用户路由 -@app.route('/register', methods=['GET', 'POST']) -@admin_required -def register(): - """ - 注册新用户(仅管理员可访问) - - GET: 显示注册页面 - POST: 处理注册表单提交 - """ - if request.method == 'POST': - username = request.form.get('username') - password = request.form.get('password') - confirm_password = request.form.get('confirm_password') - permission = int(request.form.get('permission', 1)) - - # 验证输入 - if not username or not password: - flash('请输入用户名和密码', 'error') - return render_template('register.html') - - if password != confirm_password: - flash('两次输入的密码不一致', 'error') - return render_template('register.html') - - if len(password) < 6: - flash('密码长度至少6位', 'error') - return render_template('register.html') - - # 检查用户名是否已存在 - existing_user = get_user_by_username(username) - if existing_user: - flash('用户名已存在', 'error') - return render_template('register.html') - - # 创建新用户 - success = create_user(username, password, permission) - if success: - flash(f'用户 {username} 创建成功', 'success') - return redirect(url_for('user_management')) - else: - flash('创建用户失败', 'error') - return render_template('register.html') - - return render_template('register.html') - -# 修改用户密码路由 -@app.route('/change_password/', methods=['POST']) -@admin_required -def change_password(username): - """ - 修改用户密码(仅管理员可访问) - """ - new_password = request.form.get('new_password') - confirm_password = request.form.get('confirm_password') - - if not new_password or not confirm_password: - flash('请输入新密码', 'error') - return redirect(url_for('user_management')) - - if new_password != confirm_password: - flash('两次输入的密码不一致', 'error') - return redirect(url_for('user_management')) - - if len(new_password) < 6: - flash('密码长度至少6位', 'error') - return redirect(url_for('user_management')) - - success = update_user_password(username, new_password) - if success: - flash(f'用户 {username} 密码修改成功', 'success') - else: - flash(f'修改用户 {username} 密码失败', 'error') - - return redirect(url_for('user_management')) - -# 修改用户权限路由 -@app.route('/change_permission/', methods=['POST']) -@admin_required -def change_permission(username): - """ - 修改用户权限(仅管理员可访问) - """ - new_permission = int(request.form.get('permission', 1)) - - success = update_user_permission(username, new_permission) - if success: - flash(f'用户 {username} 权限修改成功', 'success') - else: - flash(f'修改用户 {username} 权限失败', 'error') - - return redirect(url_for('user_management')) - -# 删除用户路由 -@app.route('/delete_user/', methods=['POST']) -@admin_required -def delete_user_route(username): - """ - 删除用户(仅管理员可访问) - """ - success = delete_user(username) - if success: - flash(f'用户 {username} 删除成功', 'success') - else: - flash(f'删除用户 {username} 失败', 'error') - - return redirect(url_for('user_management')) - -# 个人设置页面路由 -@app.route('/profile') -@login_required -def profile(): - """ - 显示个人设置页面 - """ - return render_template('profile.html') - -# 修改个人密码路由 -@app.route('/change_own_password', methods=['POST']) -@login_required -def change_own_password(): - """ - 用户修改自己的密码 - """ - old_password = request.form.get('old_password') - new_password = request.form.get('new_password') - confirm_password = request.form.get('confirm_password') - - # 验证输入 - if not old_password or not new_password or not confirm_password: - flash('请填写所有密码字段', 'error') - return redirect(url_for('profile')) - - if new_password != confirm_password: - flash('两次输入的新密码不一致', 'error') - return redirect(url_for('profile')) - - if len(new_password) < 6: - flash('新密码长度至少6位', 'error') - return redirect(url_for('profile')) - - # 调用修改密码函数 - success = update_user_own_password(session['user_id'], old_password, new_password) - if success: - flash('密码修改成功', 'success') - else: - flash('密码修改失败,请检查旧密码是否正确', 'error') - - return redirect(url_for('profile')) - -# 个人数据页面路由 -@app.route('/my_data') -@login_required -def my_data(): - """ - 显示用户自己的数据 - """ - user_id = session['user_id'] - keyword = request.args.get('keyword', '') - - # 查询用户自己的数据 - if keyword: - data = search_data_by_user(user_id, keyword) - else: - data = search_data_by_user(user_id) - - # 将data字段从字符串转换回JSON格式以便显示 - processed_data = [] - for item in data: - if 'data' in item and item['data']: - try: - # 将data字段的字符串转换回JSON - original_data = string_to_json(item['data']) - # 合并原始数据和其他字段 - display_item = { - '_id': item['_id'], - 'image': item.get('image', ''), - **original_data # 展开原始数据字段 - } - processed_data.append(display_item) - except Exception as e: - # 如果转换失败,保持原始格式 - processed_data.append(item) - else: - processed_data.append(item) - - return render_template('my_data.html', data=processed_data, keyword=keyword) - # 首页路由 @app.route('/') -@login_required def index(): """ 渲染首页模板 @@ -435,7 +151,6 @@ def index(): # 图片上传路由 @app.route('/upload', methods=['POST']) -@user_or_admin_required def upload_image(): """ 处理图片上传请求,调用OCR识别但不存储结果 @@ -478,7 +193,7 @@ def upload_image(): def confirm_data(): """ 确认并录入用户编辑后的数据 - + 返回: JSON: 录入成功或失败的响应 """ @@ -487,18 +202,18 @@ def confirm_data(): request_data = request.get_json() if not request_data: return jsonify({"error": "没有接收到数据"}), 400 - + # 获取编辑后的数据和图片文件名 edited_data = request_data.get('data', {}) image_filename = request_data.get('image', '') - + if not edited_data: return jsonify({"error": "数据不能为空"}), 400 - + # 使用json_converter将JSON数据转换为字符串 data_string = json_to_string(edited_data) print(f"转换后的数据字符串: {data_string}") - + # 构造新的数据结构,只包含data和image字段,并添加用户ID processed_data = { "data": data_string, @@ -519,7 +234,6 @@ def confirm_data(): # 搜索路由 @app.route('/search') -@user_or_admin_required def search(): """ 处理搜索请求,从Elasticsearch中检索匹配的数据 @@ -559,7 +273,6 @@ def search(): # 结果页面路由 @app.route('/results') -@user_or_admin_required def results_page(): """ 渲染搜索结果页面 @@ -571,7 +284,6 @@ def results_page(): # 显示所有数据路由 @app.route('/all') -@admin_required def show_all(): """ 获取所有数据并渲染到页面 @@ -619,117 +331,21 @@ def serve_image(filename): # 删除数据路由 @app.route('/delete/', methods=['POST']) -@login_required def delete_entry(doc_id): """ - 根据文档ID删除数据(用户只能删除自己的数据,管理员可以删除所有数据) + 根据文档ID删除数据 参数: doc_id (str): 要删除的文档ID 返回: - 重定向到相应页面或错误信息 + 重定向到所有数据页面或错误信息 """ - user_id = session['user_id'] - user_permission = session.get('permission', 1) - - # 管理员可以删除所有数据,普通用户只能删除自己的数据 - if user_permission == 0: # 管理员 - success = delete_by_id(doc_id) - redirect_url = 'show_all' - else: # 普通用户 - success = delete_data_by_id(doc_id, user_id) - redirect_url = 'my_data' - - if success: - return redirect(url_for(redirect_url)) + if delete_by_id(doc_id): + return redirect(url_for('show_all')) else: return "删除失败", 500 -# 编辑数据路由 -@app.route('/edit/', methods=['GET', 'POST']) -@login_required -def edit_entry(doc_id): - """ - 编辑数据条目(用户只能编辑自己的数据) - """ - if request.method == 'GET': - # 获取要编辑的数据 - try: - # 先获取文档检查权限 - response = requests.get( - f"{ES_URL}/{data_index_name}/_doc/{doc_id}", - auth=AUTH - ) - response.raise_for_status() - doc = response.json() - - if not doc.get("found"): - flash('数据不存在', 'error') - return redirect(url_for('my_data')) - - # 检查权限 - user_id = session['user_id'] - user_permission = session.get('permission', 1) - doc_user_id = doc["_source"].get("user_id") - - # 管理员可以编辑所有数据,普通用户只能编辑自己的数据 - if user_permission != 0 and doc_user_id != user_id: - flash('您无权编辑此数据', 'error') - return redirect(url_for('my_data')) - - # 解析数据 - data_str = doc["_source"].get("data", "{}") - original_data = string_to_json(data_str) - - edit_data = { - '_id': doc_id, - 'image': doc["_source"].get('image', ''), - **original_data - } - - return render_template('edit.html', data=edit_data) - - except Exception as e: - flash('获取数据失败', 'error') - return redirect(url_for('my_data')) - - else: # POST 请求 - 保存编辑 - try: - # 获取编辑后的数据 - edited_data = {} - for key, value in request.form.items(): - if key != '_id' and key != 'image': - edited_data[key] = value - - # 转换为字符串格式 - data_string = json_to_string(edited_data) - - # 构造更新数据 - updated_data = { - "data": data_string, - "image": request.form.get('image', ''), - "user_id": session['user_id'] - } - - # 更新数据 - success = update_data_by_id(doc_id, updated_data, session['user_id']) - - if success: - flash('数据更新成功', 'success') - else: - flash('数据更新失败', 'error') - - # 根据用户权限重定向 - if session.get('permission', 1) == 0: - return redirect(url_for('show_all')) - else: - return redirect(url_for('my_data')) - - except Exception as e: - flash('保存数据失败', 'error') - return redirect(url_for('my_data')) - # 主程序入口 diff --git a/templates/all.html b/templates/all.html index 1624534..514e9b5 100644 --- a/templates/all.html +++ b/templates/all.html @@ -27,50 +27,87 @@ margin-bottom: 15px; } - /* 表格容器 - 顶部边距调整 */ - .table-container { - overflow-x: auto; - margin-top: 15px; /* 减少顶部间距 */ + /* 卡片容器样式 */ + .data-cards { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); + gap: 20px; + margin-bottom: 20px; + } + + /* 卡片样式 */ + .data-card { + background-color: white; border-radius: 8px; - box-shadow: 0 4px 6px rgba(0,0,0,0.1); + box-shadow: 0 2px 8px rgba(0,0,0,0.1); + padding: 20px; + border: 1px solid #e0e0e0; + transition: transform 0.3s, box-shadow 0.3s; } - /* 表格样式 */ - table { - width: 100%; - border-collapse: collapse; - font-family: 'Segoe UI', Arial, sans-serif; + .data-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0,0,0,0.15); } - /* 表头样式 */ - thead { - background: linear-gradient(135deg, #3498db, #1a5276); - color: white; + /* 卡片头部样式 */ + .card-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; + padding-bottom: 15px; + border-bottom: 1px solid #f0f0f0; } - th { - padding: 16px 12px; - text-align: left; + .card-header h3 { + margin: 0; + color: #333; + font-size: 18px; + } + + .card-actions { + display: flex; + gap: 8px; + } + + /* 卡片内容样式 */ + .card-content { + margin-bottom: 15px; + } + + .field-item { + display: flex; + margin-bottom: 10px; + line-height: 1.5; + } + + .field-key { font-weight: 600; + color: #333; + min-width: 120px; + margin-right: 10px; } - /* 表格行样式 */ - tbody tr { - border-bottom: 1px solid #eef1f5; - transition: background-color 0.3s; + .field-value { + color: #666; + flex: 1; + word-break: break-word; } - tbody tr:nth-child(even) { - background-color: #f8fafc; + /* 卡片图片样式 */ + .card-image { + text-align: center; + margin-top: 15px; + padding-top: 15px; + border-top: 1px solid #f0f0f0; } - tbody tr:hover { - background-color: #e3f2fd; - } - - td { - padding: 14px 12px; - color: #4a5568; + .card-image img { + max-width: 100%; + max-height: 200px; + border-radius: 4px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); } /* 操作按钮样式 */ @@ -81,6 +118,17 @@ cursor: pointer; font-weight: 500; transition: all 0.3s; + margin: 0 2px; + } + + .edit-btn { + background: linear-gradient(to right, #4CAF50, #45a049); + color: white; + } + + .edit-btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(76, 175, 80, 0.3); } .delete-btn { @@ -117,48 +165,189 @@ padding: 40px 0; color: #a0aec0; font-style: italic; + grid-column: 1 / -1; + } + + /* 响应式设计 */ + @media (max-width: 768px) { + .data-cards { + grid-template-columns: 1fr; + } + + .card-header { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + + .card-actions { + align-self: flex-end; + } + + .field-item { + flex-direction: column; + } + + .field-key { + min-width: auto; + margin-bottom: 5px; + } }

所有已录入的奖项信息

-

在此页面可以查看所有已录入的成果信息,并进行删除操作

+

在此页面可以查看所有已录入的成果信息,并进行编辑和删除操作

-
- - - - - - - - - - - - {% if data %} - {% for item in data %} - - - - - - - - {% endfor %} - {% else %} - - - - {% endif %} - -
比赛/论文名称项目名称学生指导老师操作
{{ item.id or '无' }}{{ item.name or '无' }}{% if item.students is string %}{{ item.students or '无' }}{% else %}{{ item.students|join(', ') if item.students else '无' }}{% endif %}{% if item.teacher is string %}{{ item.teacher or '无' }}{% else %}{{ item.teacher|join(', ') if item.teacher else '无' }}{% endif %} -
- -
-
暂无数据
+ +
+
+
+ + +
+ + 已选择 0 项 +
+
+ +
+ {% if data %} + {% for item in data %} +
+
+
+ +

记录 {{ loop.index }}

+
+
+ 编辑 +
+
+ +
+ {% if item.data %} + {# 从原始数据中解析字段 #} + {% set data_string = item.data %} + {% set pairs = data_string.split('|###|') %} + + {% for pair in pairs %} + {% if ':' in pair %} + {% set key_value = pair.split(':', 1) %} + {% set field_key = key_value[0].strip() %} + {% set field_value = key_value[1].strip() %} + + {# 处理列表格式 [item1|##|item2] #} + {% if field_value.startswith('[') and field_value.endswith(']') %} + {% set list_content = field_value[1:-1] %} + {% set field_value = list_content.split('|##|')|join(', ') %} + {% endif %} + +
+ {{ field_key }}: + {{ field_value or '无' }} +
+ {% endif %} + {% endfor %} + {% else %} + {# 如果没有data字段,显示解析后的字段 #} + {% for key, value in item.items() %} + {% if key not in ['_id', 'image'] %} +
+ {{ key }}: + + {% if value is sequence and value is not string %} + {{ value|join(', ') if value else '无' }} + {% else %} + {{ value or '无' }} + {% endif %} + +
+ {% endif %} + {% endfor %} + {% endif %} +
+ + +
+ {% endfor %} + {% else %} +
暂无数据
+ {% endif %}
返回首页
+ + + {% endblock %} \ No newline at end of file diff --git a/templates/edited.html b/templates/edited.html new file mode 100644 index 0000000..8125021 --- /dev/null +++ b/templates/edited.html @@ -0,0 +1,256 @@ +{% extends "base.html" %} + +{% block title %}编辑成果信息 - 紫金·稷下薪火·云枢智海师生成果共创系统{% endblock %} + +{% block content %} + + +
+

编辑成果信息

+ +
+
+ {% if document.data %} + {# 从原始数据中解析字段 #} + {% set data_string = document.data %} + {% set pairs = data_string.split('|###|') %} + + {% for pair in pairs %} + {% if ':' in pair %} + {% set key_value = pair.split(':', 1) %} + {% set field_key = key_value[0].strip() %} + {% set field_value = key_value[1].strip() %} + + {# 处理列表格式 [item1|##|item2] #} + {% if field_value.startswith('[') and field_value.endswith(']') %} + {% set list_content = field_value[1:-1] %} + {% set field_value = list_content.split('|##|')|join(', ') %} + {% endif %} + +
+ + + +
+ {% endif %} + {% endfor %} + {% else %} + {# 如果没有data字段,显示提示信息 #} +
+

该记录没有可编辑的数据

+
+ {% endif %} + + {% if document.image %} +
+ +
+ 原图片 +
+
当前关联的图片,编辑时无法修改图片
+
+ {% endif %} + +
+ + 取消返回 + +
+
+
+
+ + +{% endblock %} \ No newline at end of file