You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
4.9 KiB
113 lines
4.9 KiB
import os
|
|
import json
|
|
from flask import Flask, render_template_string, request, redirect, url_for, flash
|
|
|
|
app = Flask(__name__)
|
|
app.secret_key = "supersecretkey"
|
|
|
|
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
VOCAB_DIR = os.path.join(BASE_DIR, "data", "vocab")
|
|
CONFIG_DIR = os.path.join(BASE_DIR, "data", "config")
|
|
|
|
FILES = {
|
|
"l1": os.path.join(VOCAB_DIR, "l1_standard.json"),
|
|
"l2": os.path.join(VOCAB_DIR, "l2_hospital.json"),
|
|
"l3": os.path.join(VOCAB_DIR, "l3_mapping.json"),
|
|
"pinyin": os.path.join(VOCAB_DIR, "pinyin_map.json"),
|
|
"scoring": os.path.join(CONFIG_DIR, "scoring_standard.json")
|
|
}
|
|
|
|
HTML_TEMPLATE = """
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>医疗影像AI质控 - 后台管理</title>
|
|
<style>
|
|
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f7f6; margin: 0; padding: 20px; }
|
|
.container { max-width: 1000px; margin: auto; background: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); }
|
|
h1 { color: #2c3e50; text-align: center; border-bottom: 2px solid #3498db; padding-bottom: 15px; }
|
|
.nav { display: flex; justify-content: center; gap: 10px; margin-bottom: 25px; }
|
|
.nav a { text-decoration: none; color: #34495e; padding: 8px 16px; border-radius: 6px; background: #ecf0f1; transition: 0.3s; }
|
|
.nav a:hover, .nav a.active { background: #3498db; color: white; }
|
|
.editor-area { margin-top: 20px; }
|
|
textarea { width: 100%; height: 500px; font-family: monospace; font-size: 14px; padding: 15px; border: 2px solid #bdc3c7; border-radius: 8px; box-sizing: border-box; }
|
|
.btn-save { display: block; width: 100%; padding: 12px; background: #27ae60; color: white; border: none; border-radius: 8px; font-size: 16px; font-weight: bold; cursor: pointer; margin-top: 15px; transition: 0.3s; }
|
|
.btn-save:hover { background: #219150; }
|
|
.alert { padding: 15px; margin-bottom: 20px; border-radius: 6px; }
|
|
.alert-success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
|
|
.alert-error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>🏥 医疗影像质控后台管理</h1>
|
|
<div class="nav">
|
|
<a href="{{ url_for('edit', file_key='l1') }}" class="{{ 'active' if current == 'l1' }}">L1 标准术语</a>
|
|
<a href="{{ url_for('edit', file_key='l2') }}" class="{{ 'active' if current == 'l2' }}">L2 本院特色</a>
|
|
<a href="{{ url_for('edit', file_key='l3') }}" class="{{ 'active' if current == 'l3' }}">L3 纠错对照</a>
|
|
<a href="{{ url_for('edit', file_key='pinyin') }}" class="{{ 'active' if current == 'pinyin' }}">拼音映射</a>
|
|
<a href="{{ url_for('edit', file_key='scoring') }}" class="{{ 'active' if current == 'scoring' }}">评分引擎配置</a>
|
|
</div>
|
|
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
{% for category, message in messages %}
|
|
<div class="alert alert-{{ category }}">{{ message }}</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endwith %}
|
|
|
|
<div class="editor-area">
|
|
<h3>正在编辑:{{ file_name }}</h3>
|
|
<form method="POST">
|
|
<textarea name="content">{{ content }}</textarea>
|
|
<button type="submit" class="btn-save">保存修改并同步到 RuleEngine</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
"""
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return redirect(url_for('edit', file_key='l1'))
|
|
|
|
@app.route('/edit/<file_key>', methods=['GET', 'POST'])
|
|
def edit(file_key):
|
|
if file_key not in FILES:
|
|
return "File not found", 404
|
|
|
|
file_path = FILES[file_key]
|
|
|
|
if request.method == 'POST':
|
|
content = request.form.get('content')
|
|
try:
|
|
# Validate JSON
|
|
json_data = json.loads(content)
|
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
json.dump(json_data, f, ensure_ascii=False, indent=2)
|
|
flash("保存成功!RuleEngine 已实时同步最新规则。", "success")
|
|
except Exception as e:
|
|
flash(f"保存失败:JSON 格式错误 ( {str(e)} )", "error")
|
|
return redirect(url_for('edit', file_key=file_key))
|
|
|
|
# GET
|
|
if os.path.exists(file_path):
|
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
else:
|
|
content = "{}" if file_key != "l1" and file_key != "l2" else "[]"
|
|
|
|
return render_template_string(
|
|
HTML_TEMPLATE,
|
|
content=content,
|
|
current=file_key,
|
|
file_name=os.path.basename(file_path)
|
|
)
|
|
|
|
if __name__ == '__main__':
|
|
# Ensure directories exist
|
|
os.makedirs(VOCAB_DIR, exist_ok=True)
|
|
os.makedirs(CONFIG_DIR, exist_ok=True)
|
|
app.run(port=5005, debug=True)
|
|
|