Compare commits

..

2 Commits

Author SHA1 Message Date
5a6f799059 新增“数据编辑” 2025-10-06 22:04:48 +08:00
8c530ff599 新增“数据编辑” 2025-10-02 15:49:36 +08:00
4 changed files with 420 additions and 4 deletions

View File

@@ -40,6 +40,9 @@ def create_index_with_mapping():
else: else:
print(f"索引 {index_name} 已存在") print(f"索引 {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): def get_doc_id(data):
@@ -122,6 +125,49 @@ def delete_by_id(doc_id):
print("删除失败:", str(e)) print("删除失败:", str(e))
return False 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): def search_by_any_field(keyword):
"""全字段模糊搜索(支持拼写错误)""" """全字段模糊搜索(支持拼写错误)"""
try: try:

100
app.py
View File

@@ -276,6 +276,83 @@ def show_all():
return render_template('all.html', data=processed_data) return render_template('all.html', data=processed_data)
# 编辑数据页面路由
@app.route('/edit/<doc_id>')
def edit_entry(doc_id):
"""
渲染编辑页面
参数:
doc_id (str): 要编辑的文档ID
返回:
str: 渲染后的编辑页面或错误信息
"""
# 获取要编辑的文档数据
document = get_by_id(doc_id)
if not document:
return "文档不存在", 404
# 保持原始数据格式不进行JSON转换
# 直接传递包含data字段的原始文档
return render_template('edit.html', document=document)
# 更新数据路由
@app.route('/update/<doc_id>', methods=['POST'])
def update_entry(doc_id):
"""
处理数据更新请求
参数:
doc_id (str): 要更新的文档ID
返回:
重定向到所有数据页面或错误信息
"""
# 获取原文档的图片信息
original_doc = get_by_id(doc_id)
if not original_doc:
return "文档不存在", 404
# 从表单中获取所有字段数据
data_parts = []
i = 1
while True:
key_name = request.form.get(f'key_{i}')
field_value = request.form.get(f'field_{i}')
if not key_name or not field_value:
break
# 处理字段值(如果是列表格式,用|##|分隔)
if ',' in field_value:
# 如果是逗号分隔的值,转换为列表格式
items = [item.strip() for item in field_value.split(',') if item.strip()]
if len(items) > 1:
field_value = f"[{'|##|'.join(items)}]"
data_parts.append(f"{key_name}:{field_value}")
i += 1
# 验证是否有数据
if not data_parts:
return "没有可更新的数据", 400
# 构建新的数据字符串
data_value = "|###|".join(data_parts)
# 构造更新数据
updated_data = {
'data': data_value,
'image': original_doc.get('image', '') # 保持原图片
}
# 更新文档
if update_by_id(doc_id, updated_data):
return redirect(url_for('show_all'))
else:
return "更新失败", 500
# 删除数据路由 # 删除数据路由
@app.route('/delete/<doc_id>', methods=['POST']) @app.route('/delete/<doc_id>', methods=['POST'])
def delete_entry(doc_id): def delete_entry(doc_id):
@@ -295,6 +372,29 @@ def delete_entry(doc_id):
# 提供图片访问的路由
@app.route('/image/<filename>')
def serve_image(filename):
"""
提供image目录下图片的访问服务
参数:
filename (str): 图片文件名
返回:
图片文件或404错误
"""
import os
from flask import send_from_directory
# 确保文件存在
image_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'image')
if not os.path.exists(os.path.join(image_dir, filename)):
return "图片不存在", 404
# 发送图片文件
return send_from_directory(image_dir, filename)
# 主程序入口 # 主程序入口
if __name__ == '__main__': if __name__ == '__main__':
# 创建Elasticsearch索引 # 创建Elasticsearch索引

View File

@@ -81,6 +81,17 @@
cursor: pointer; cursor: pointer;
font-weight: 500; font-weight: 500;
transition: all 0.3s; 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 { .delete-btn {
@@ -122,7 +133,7 @@
<div class="container"> <div class="container">
<h2>所有已录入的奖项信息</h2> <h2>所有已录入的奖项信息</h2>
<p>在此页面可以查看所有已录入的成果信息,并进行删除操作</p> <p>在此页面可以查看所有已录入的成果信息,并进行编辑和删除操作</p>
<div class="table-container"> <div class="table-container">
<table> <table>
@@ -144,9 +155,12 @@
<td>{% if item.students is string %}{{ item.students or '无' }}{% else %}{{ item.students|join(', ') if item.students else '无' }}{% endif %}</td> <td>{% if item.students is string %}{{ item.students or '无' }}{% else %}{{ item.students|join(', ') if item.students else '无' }}{% endif %}</td>
<td>{% if item.teacher is string %}{{ item.teacher or '无' }}{% else %}{{ item.teacher|join(', ') if item.teacher else '无' }}{% endif %}</td> <td>{% if item.teacher is string %}{{ item.teacher or '无' }}{% else %}{{ item.teacher|join(', ') if item.teacher else '无' }}{% endif %}</td>
<td style="text-align: center;"> <td style="text-align: center;">
<form action="{{ url_for('delete_entry', doc_id=item._id) }}" method="POST" onsubmit="return confirm('确定要删除这条记录吗?')"> <div style="display: flex; justify-content: center; gap: 8px;">
<button type="submit" class="action-button delete-btn">删除</button> <a href="{{ url_for('edit_entry', doc_id=item._id) }}" class="action-button edit-btn">编辑</a>
</form> <form action="{{ url_for('delete_entry', doc_id=item._id) }}" method="POST" onsubmit="return confirm('确定要删除这条记录吗?')" style="margin: 0;">
<button type="submit" class="action-button delete-btn">删除</button>
</form>
</div>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

256
templates/edit.html Normal file
View File

@@ -0,0 +1,256 @@
{% extends "base.html" %}
{% block title %}编辑数据 - 紫金·稷下薪火·云枢智海师生成果共创系统{% endblock %}
{% block content %}
<style>
/* 基础样式重置 */
* { margin: 0; padding: 0; box-sizing: border-box; }
/* 容器样式 */
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
/* 标题样式 */
h2 {
color: #2c3e50;
border-bottom: 2px solid #3498db;
padding-bottom: 8px;
margin-bottom: 20px;
}
/* 表单样式 */
.form-container {
background: white;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
padding: 30px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #2c3e50;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px;
border: 2px solid #e1e8ed;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #3498db;
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
.form-group textarea {
resize: vertical;
min-height: 80px;
}
.form-hint {
font-size: 12px;
color: #7f8c8d;
margin-top: 5px;
}
/* 按钮样式 */
.button-group {
display: flex;
gap: 15px;
margin-top: 30px;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
display: inline-block;
text-align: center;
}
.btn-primary {
background: linear-gradient(to right, #3498db, #2980b9);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(52, 152, 219, 0.3);
}
.btn-secondary {
background: linear-gradient(to right, #95a5a6, #7f8c8d);
color: white;
}
.btn-secondary:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(149, 165, 166, 0.3);
}
.btn-danger {
background: linear-gradient(to right, #e74c3c, #c0392b);
color: white;
}
.btn-danger:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(231, 76, 60, 0.3);
}
/* 图片预览样式 */
.image-preview {
margin-top: 10px;
text-align: center;
}
.image-preview img {
max-width: 200px;
max-height: 200px;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* 错误提示样式 */
.error-message {
color: #e74c3c;
font-size: 12px;
margin-top: 5px;
}
/* 必填字段标记 */
.required {
color: #e74c3c;
}
</style>
<div class="container">
<h2>编辑成果信息</h2>
<div class="form-container">
<form action="{{ url_for('update_entry', doc_id=document._id) }}" method="POST" id="editForm">
{% 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 %}
<div class="form-group">
<label for="field_{{ loop.index }}">{{ field_key }} <span class="required">*</span></label>
<input type="text" id="field_{{ loop.index }}" name="field_{{ loop.index }}" value="{{ field_value }}" required>
<input type="hidden" name="key_{{ loop.index }}" value="{{ field_key }}">
</div>
{% endif %}
{% endfor %}
{% else %}
{# 如果没有data字段显示提示信息 #}
<div class="form-group">
<p style="color: #e74c3c; text-align: center;">该记录没有可编辑的数据</p>
</div>
{% endif %}
{% if document.image %}
<div class="form-group">
<label>原图片预览</label>
<div class="image-preview">
<img src="{{ url_for('serve_image', filename=document.image) }}" alt="原图片" onerror="this.style.display='none'">
</div>
<div class="form-hint">当前关联的图片,编辑时无法修改图片</div>
</div>
{% endif %}
<div class="button-group">
<button type="submit" class="btn btn-primary">保存修改</button>
<a href="{{ url_for('show_all') }}" class="btn btn-secondary">取消返回</a>
<button type="button" class="btn btn-danger" onclick="confirmDelete()">删除记录</button>
</div>
</form>
</div>
</div>
<script>
// 表单验证
document.getElementById('editForm').addEventListener('submit', function(e) {
// 检查所有字段是否都有值
const inputs = document.querySelectorAll('input[type="text"]');
let hasEmptyField = false;
inputs.forEach(input => {
if (!input.value.trim()) {
hasEmptyField = true;
input.style.borderColor = '#e74c3c';
} else {
input.style.borderColor = '#e1e8ed';
}
});
if (hasEmptyField) {
e.preventDefault();
alert('所有字段都必须填写!');
return false;
}
return true;
});
// 删除确认
function confirmDelete() {
if (confirm('确定要删除这条记录吗?此操作不可撤销!')) {
// 创建删除表单并提交
const form = document.createElement('form');
form.method = 'POST';
form.action = '{{ url_for("delete_entry", doc_id=document._id) }}';
document.body.appendChild(form);
form.submit();
}
}
// 自动格式化逗号分隔的值
document.querySelectorAll('input[type="text"]').forEach(input => {
input.addEventListener('blur', function(e) {
const value = e.target.value.trim();
if (value && value.includes(',')) {
// 格式化逗号分隔的值
const formatted = value
.split(',')
.map(item => item.trim())
.filter(item => item)
.join(', ');
e.target.value = formatted;
}
});
});
</script>
{% endblock %}