first commit

This commit is contained in:
rjb
2025-12-20 23:19:19 +08:00
parent 4129d63afc
commit b728d729e6
14 changed files with 2850 additions and 2101 deletions

View File

@@ -1,463 +0,0 @@
"""
优化历史功能API接口
使用腾讯云prompt表
"""
from flask import Blueprint, request, jsonify, current_app, render_template
from flask_login import login_required, current_user
from datetime import datetime, date, timedelta
from sqlalchemy import func, desc, or_
import time
import pymysql
# 导入数据库模型
from ..models.models import db, Prompt, User, WxUser
# 创建蓝图
optimization_history_bp = Blueprint('optimization_history', __name__)
def get_current_user_id():
"""获取当前用户ID如果没有登录用户则返回默认用户ID"""
try:
return current_user.id if current_user.is_authenticated else 1
except:
return 1
def get_tencent_db_connection():
"""获取腾讯云数据库连接"""
try:
conn = pymysql.connect(
host='gz-cynosdbmysql-grp-d26pzce5.sql.tencentcdb.com',
port=24936,
user='root',
password='!Rjb12191',
database='pro_db',
charset='utf8mb4'
)
return conn
except Exception as e:
current_app.logger.error(f"连接腾讯云数据库失败: {str(e)}")
return None
@optimization_history_bp.route('/optimization-history')
# @login_required # 暂时注释掉登录要求,用于测试
def optimization_history_page():
"""优化历史页面"""
return render_template('optimization_history.html')
@optimization_history_bp.route('/api/optimization-history', methods=['GET'])
# @login_required # 暂时注释掉登录要求,用于测试
def get_optimization_history():
"""获取用户优化历史记录 - 使用腾讯云prompt表"""
try:
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 20, type=int)
search = request.args.get('search', '')
date_filter = request.args.get('date_filter', '')
# 获取腾讯云数据库连接
conn = get_tencent_db_connection()
if not conn:
return jsonify({
'success': False,
'message': '数据库连接失败'
}), 500
cursor = conn.cursor()
# 构建查询条件
where_conditions = []
params = []
# 用户ID条件
user_id = get_current_user_id()
where_conditions.append("(user_id = %s OR wx_user_id = %s)")
params.extend([user_id, user_id])
# 搜索条件
if search:
where_conditions.append("(input_text LIKE %s OR generated_text LIKE %s)")
search_param = f"%{search}%"
params.extend([search_param, search_param])
# 日期过滤
if date_filter:
if date_filter == 'today':
where_conditions.append("DATE(created_at) = CURDATE()")
elif date_filter == 'week':
where_conditions.append("created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)")
elif date_filter == 'month':
where_conditions.append("created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)")
# 构建完整查询
where_clause = " AND ".join(where_conditions) if where_conditions else "1=1"
# 获取总数
count_sql = f"SELECT COUNT(*) FROM prompt WHERE {where_clause}"
cursor.execute(count_sql, params)
total = cursor.fetchone()[0]
# 计算分页
offset = (page - 1) * per_page
total_pages = (total + per_page - 1) // per_page
# 获取数据
data_sql = f"""
SELECT id, input_text, generated_text, created_at, user_id, wx_user_id
FROM prompt
WHERE {where_clause}
ORDER BY created_at DESC
LIMIT %s OFFSET %s
"""
cursor.execute(data_sql, params + [per_page, offset])
results = cursor.fetchall()
# 格式化数据
history_list = []
for row in results:
history_dict = {
'id': row[0],
'original_text': row[1],
'optimized_text': row[2],
'created_at': row[3].strftime('%Y-%m-%d %H:%M:%S') if row[3] else None,
'user_id': row[4],
'wx_user_id': row[5],
'optimization_type': '提示词优化',
'satisfaction_rating': None,
'is_favorite': False
}
history_list.append(history_dict)
cursor.close()
conn.close()
return jsonify({
'success': True,
'data': {
'history': history_list,
'pagination': {
'page': page,
'per_page': per_page,
'total': total,
'pages': total_pages,
'has_next': page < total_pages,
'has_prev': page > 1
}
}
})
except Exception as e:
current_app.logger.error(f"获取优化历史失败: {str(e)}")
return jsonify({
'success': False,
'message': '获取历史记录失败'
}), 500
@optimization_history_bp.route('/api/optimization-history', methods=['POST'])
# @login_required # 暂时注释掉登录要求,用于测试
def add_optimization_history():
"""添加优化历史记录 - 使用腾讯云prompt表"""
try:
data = request.get_json()
# 验证必需字段
required_fields = ['original_text', 'optimized_text']
for field in required_fields:
if field not in data or not data[field]:
return jsonify({
'success': False,
'message': f'缺少必需字段: {field}'
}), 400
# 获取腾讯云数据库连接
conn = get_tencent_db_connection()
if not conn:
return jsonify({
'success': False,
'message': '数据库连接失败'
}), 500
cursor = conn.cursor()
# 获取用户ID
user_id = get_current_user_id()
# 插入数据到prompt表
insert_sql = """
INSERT INTO prompt (input_text, generated_text, user_id, wx_user_id, created_at)
VALUES (%s, %s, %s, %s, NOW())
"""
cursor.execute(insert_sql, [
data['original_text'],
data['optimized_text'],
user_id,
data.get('wx_user_id', user_id) # 如果有微信用户ID则使用否则使用普通用户ID
])
# 获取插入的记录ID
prompt_id = cursor.lastrowid
conn.commit()
cursor.close()
conn.close()
# 返回结果
result_data = {
'id': prompt_id,
'original_text': data['original_text'],
'optimized_text': data['optimized_text'],
'user_id': user_id,
'wx_user_id': data.get('wx_user_id', user_id),
'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'optimization_type': '提示词优化',
'satisfaction_rating': None,
'is_favorite': False
}
return jsonify({
'success': True,
'data': result_data,
'message': '历史记录添加成功'
})
except Exception as e:
import traceback
current_app.logger.error(f"添加优化历史失败: {str(e)}")
current_app.logger.error(f"错误堆栈: {traceback.format_exc()}")
return jsonify({
'success': False,
'message': f'添加历史记录失败: {str(e)}'
}), 500
@optimization_history_bp.route('/api/optimization-history/<int:history_id>/rating', methods=['PUT'])
# @login_required # 暂时注释掉登录要求,用于测试
def update_rating(history_id):
"""更新满意度评分 - 简化版本,仅返回成功"""
try:
data = request.get_json()
rating = data.get('rating')
if not rating or not isinstance(rating, int) or rating < 1 or rating > 5:
return jsonify({
'success': False,
'message': '评分必须在1-5之间'
}), 400
# 由于prompt表没有评分字段这里仅返回成功
# 在实际应用中可以考虑添加评分字段到prompt表
return jsonify({
'success': True,
'message': '评分记录成功(注意:当前版本暂不支持评分存储)'
})
except Exception as e:
current_app.logger.error(f"更新评分失败: {str(e)}")
return jsonify({
'success': False,
'message': '更新评分失败'
}), 500
@optimization_history_bp.route('/api/optimization-history/<int:history_id>', methods=['DELETE'])
# @login_required # 暂时注释掉登录要求,用于测试
def delete_optimization_history(history_id):
"""删除优化历史记录 - 使用腾讯云prompt表"""
try:
# 获取腾讯云数据库连接
conn = get_tencent_db_connection()
if not conn:
return jsonify({
'success': False,
'message': '数据库连接失败'
}), 500
cursor = conn.cursor()
# 检查记录是否存在
check_sql = "SELECT id FROM prompt WHERE id = %s"
cursor.execute(check_sql, [history_id])
if not cursor.fetchone():
cursor.close()
conn.close()
return jsonify({
'success': False,
'message': '历史记录不存在'
}), 404
# 删除记录
delete_sql = "DELETE FROM prompt WHERE id = %s"
cursor.execute(delete_sql, [history_id])
conn.commit()
cursor.close()
conn.close()
return jsonify({
'success': True,
'message': '历史记录删除成功'
})
except Exception as e:
current_app.logger.error(f"删除优化历史失败: {str(e)}")
return jsonify({
'success': False,
'message': '删除历史记录失败'
}), 500
@optimization_history_bp.route('/api/optimization-history/clear', methods=['DELETE'])
# @login_required # 暂时注释掉登录要求,用于测试
def clear_optimization_history():
"""清空用户优化历史记录 - 使用腾讯云prompt表"""
try:
# 获取腾讯云数据库连接
conn = get_tencent_db_connection()
if not conn:
return jsonify({
'success': False,
'message': '数据库连接失败'
}), 500
cursor = conn.cursor()
# 获取用户ID
user_id = get_current_user_id()
# 删除用户的所有记录
delete_sql = "DELETE FROM prompt WHERE user_id = %s OR wx_user_id = %s"
cursor.execute(delete_sql, [user_id, user_id])
deleted_count = cursor.rowcount
conn.commit()
cursor.close()
conn.close()
return jsonify({
'success': True,
'message': f'历史记录清空成功,共删除 {deleted_count} 条记录'
})
except Exception as e:
current_app.logger.error(f"清空优化历史失败: {str(e)}")
return jsonify({
'success': False,
'message': '清空历史记录失败'
}), 500
@optimization_history_bp.route('/api/optimization-history/stats', methods=['GET'])
# @login_required # 暂时注释掉登录要求,用于测试
def get_user_stats():
"""获取用户使用统计 - 简化版本"""
try:
# 获取腾讯云数据库连接
conn = get_tencent_db_connection()
if not conn:
return jsonify({
'success': False,
'message': '数据库连接失败'
}), 500
cursor = conn.cursor()
user_id = get_current_user_id()
# 获取基本统计信息
stats_sql = """
SELECT
COUNT(*) as total_count,
COUNT(CASE WHEN DATE(created_at) = CURDATE() THEN 1 END) as today_count,
COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 END) as week_count,
COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as month_count
FROM prompt
WHERE user_id = %s OR wx_user_id = %s
"""
cursor.execute(stats_sql, [user_id, user_id])
stats = cursor.fetchone()
cursor.close()
conn.close()
return jsonify({
'success': True,
'data': {
'total_count': stats[0] or 0,
'today_count': stats[1] or 0,
'week_count': stats[2] or 0,
'month_count': stats[3] or 0
}
})
except Exception as e:
current_app.logger.error(f"获取用户统计失败: {str(e)}")
return jsonify({
'success': False,
'message': '获取统计信息失败'
}), 500
@optimization_history_bp.route('/api/optimization-history/export', methods=['GET'])
# @login_required # 暂时注释掉登录要求,用于测试
def export_history():
"""导出历史记录 - 使用腾讯云prompt表"""
try:
format_type = request.args.get('format', 'json')
# 获取腾讯云数据库连接
conn = get_tencent_db_connection()
if not conn:
return jsonify({
'success': False,
'message': '数据库连接失败'
}), 500
cursor = conn.cursor()
user_id = get_current_user_id()
# 获取所有历史记录
export_sql = """
SELECT id, input_text, generated_text, created_at, user_id, wx_user_id
FROM prompt
WHERE user_id = %s OR wx_user_id = %s
ORDER BY created_at DESC
"""
cursor.execute(export_sql, [user_id, user_id])
results = cursor.fetchall()
# 格式化数据
history_list = []
for row in results:
history_dict = {
'id': row[0],
'original_text': row[1],
'optimized_text': row[2],
'created_at': row[3].strftime('%Y-%m-%d %H:%M:%S') if row[3] else None,
'user_id': row[4],
'wx_user_id': row[5],
'optimization_type': '提示词优化'
}
history_list.append(history_dict)
cursor.close()
conn.close()
if format_type == 'json':
return jsonify({
'success': True,
'data': history_list,
'count': len(history_list)
})
else:
return jsonify({
'success': False,
'message': '不支持的导出格式'
}), 400
except Exception as e:
current_app.logger.error(f"导出历史记录失败: {str(e)}")
return jsonify({
'success': False,
'message': '导出历史记录失败'
}), 500

View File

@@ -56,11 +56,11 @@ def generate_with_llm(input_text, template_id=None, max_retries=3):
start_time = time.time() # 记录开始时间
# 记录参数到日志
logger.info("=== API 调用参数 ===")
logger.info(f"模板ID: {template_id}")
logger.info(f"输入文本: {input_text}")
logger.info(f"系统提示: {system_prompt}")
logger.info("==================")
current_app.logger.info("=== API 调用参数 ===")
current_app.logger.info(f"模板ID: {template_id}")
current_app.logger.info(f"输入文本: {input_text}")
current_app.logger.info(f"系统提示: {system_prompt}")
current_app.logger.info("==================")
for attempt in range(max_retries):
try:
@@ -80,10 +80,10 @@ def generate_with_llm(input_text, template_id=None, max_retries=3):
# 打印响应
generated_text = response.choices[0].message.content.strip()
print("\n=== API 响应结果 ===")
print(f"生成的提示词: {generated_text}")
print(f"生成耗时: {generation_time}ms")
print("==================\n")
current_app.logger.info("=== API 响应结果 ===")
current_app.logger.info(f"生成的提示词: {generated_text}")
current_app.logger.info(f"生成耗时: {generation_time}ms")
current_app.logger.info("==================")
# 保存到历史记录
save_to_history(
@@ -197,46 +197,69 @@ def index():
sub_categories = sorted(set(t.sub_category for t in all_templates if t.sub_category))
if form.validate_on_submit():
template_id = request.form.get('template_id')
generated_text = generate_with_llm(form.input_text.data, template_id)
# 获取搜索状态
search_state = request.form.get('search_state', '')
# 获取默认用户的 uid
try:
conn = pymysql.connect(
host='localhost',
user='root',
password='123456',
database='pro_db',
charset='utf8mb4'
template_id = request.form.get('template_id')
input_text = form.input_text.data.strip()
# 验证输入
if not input_text:
flash('请输入您的需求描述', 'error')
return render_template('generate.html', form=form, prompt=None, templates=templates,
get_template_icon=get_template_icon, industries=industries,
professions=professions, categories=categories,
sub_categories=sub_categories)
# 调用LLM生成提示词
generated_text = generate_with_llm(input_text, template_id)
# 检查生成结果
if not generated_text or generated_text.startswith("提示词生成失败"):
flash(f'生成失败: {generated_text}', 'error')
return render_template('generate.html', form=form, prompt=None, templates=templates,
get_template_icon=get_template_icon, industries=industries,
professions=professions, categories=categories,
sub_categories=sub_categories)
# 获取搜索状态
search_state = request.form.get('search_state', '')
# 获取默认用户的 uid - 使用SQLAlchemy而不是直接连接
try:
# 尝试从现有用户表中获取admin用户
admin_user = User.query.filter_by(login_name='admin').first()
if admin_user:
user_id = admin_user.uid
else:
# 如果没有admin用户使用默认值
user_id = 1
except Exception as e:
current_app.logger.warning(f"获取用户ID失败: {str(e)}")
user_id = 1 # 如果查询失败,使用默认值
# 保存到数据库
prompt = Prompt(
input_text=input_text,
generated_text=generated_text,
user_id=user_id
)
cursor = conn.cursor()
cursor.execute("SELECT uid FROM user WHERE login_name = 'admin' LIMIT 1")
result = cursor.fetchone()
if result:
user_id = result[0]
else:
user_id = 1 # 如果没有找到用户,使用默认值
cursor.close()
conn.close()
db.session.add(prompt)
db.session.commit()
flash('提示词生成成功!', 'success')
return render_template('generate.html', form=form, prompt=prompt, templates=templates,
get_template_icon=get_template_icon, industries=industries,
professions=professions, categories=categories,
sub_categories=sub_categories, selected_template_id=template_id,
search_state=search_state)
except Exception as e:
print(f"获取用户ID失败: {str(e)}")
user_id = 1 # 如果查询失败,使用默认值
prompt = Prompt(
input_text=form.input_text.data,
generated_text=generated_text,
user_id=user_id # 使用查询到的用户ID
)
db.session.add(prompt)
db.session.commit()
return render_template('generate.html', form=form, prompt=prompt, templates=templates,
get_template_icon=get_template_icon, industries=industries,
professions=professions, categories=categories,
sub_categories=sub_categories, selected_template_id=template_id,
search_state=search_state)
current_app.logger.error(f"生成提示词时发生错误: {str(e)}")
db.session.rollback()
flash(f'生成提示词时发生错误: {str(e)}', 'error')
return render_template('generate.html', form=form, prompt=None, templates=templates,
get_template_icon=get_template_icon, industries=industries,
professions=professions, categories=categories,
sub_categories=sub_categories)
return render_template('generate.html', form=form, prompt=None, templates=templates,
get_template_icon=get_template_icon, industries=industries,
professions=professions, categories=categories,

View File

@@ -1,846 +0,0 @@
{% extends "base.html" %}
{% block title %}优化历史 - AI应用{% endblock %}
{% block extra_css %}
<style>
/* 优化历史页面样式 - 参考收藏页面设计 */
.history-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #1E3A8A 0%, #3B82F6 100%);
min-height: 100vh;
}
.history-header {
text-align: center;
color: white;
margin-bottom: 40px;
}
.history-header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
font-family: 'Inter', sans-serif;
font-weight: 700;
}
.history-header p {
font-size: 1.2rem;
opacity: 0.9;
font-family: 'Inter', sans-serif;
}
.history-content {
background: white;
border-radius: 20px;
padding: 40px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
}
/* 搜索和筛选区域 */
.search-filters {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
padding: 20px;
background: #f8f9fa;
border-radius: 15px;
}
.search-filters input,
.search-filters select {
padding: 12px 16px;
border: 2px solid #e1e5e9;
border-radius: 10px;
font-size: 1rem;
transition: all 0.3s ease;
font-family: 'Inter', sans-serif;
}
.search-filters input:focus,
.search-filters select:focus {
outline: none;
border-color: #3B82F6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.search-btn {
background: linear-gradient(135deg, #3B82F6, #1E3A8A);
color: white;
border: none;
padding: 12px 24px;
border-radius: 10px;
font-weight: 600;
transition: all 0.3s ease;
cursor: pointer;
}
.search-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(59, 130, 246, 0.3);
}
/* 历史记录列表 */
.history-list {
display: grid;
gap: 20px;
}
.history-item {
background: #f8f9fa;
border: 2px solid #e1e5e9;
border-radius: 15px;
padding: 25px;
transition: all 0.3s ease;
position: relative;
}
.history-item:hover {
border-color: #3B82F6;
box-shadow: 0 5px 15px rgba(59, 130, 246, 0.1);
transform: translateY(-2px);
}
.history-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 15px;
}
.history-title {
font-size: 1.3rem;
font-weight: 700;
color: #333;
margin-bottom: 5px;
font-family: 'Inter', sans-serif;
}
.history-type {
color: #3B82F6;
font-size: 1rem;
font-weight: 600;
background: rgba(59, 130, 246, 0.1);
padding: 4px 12px;
border-radius: 20px;
}
.history-meta {
display: flex;
gap: 15px;
align-items: center;
margin-bottom: 15px;
flex-wrap: wrap;
}
.history-time {
color: #666;
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 5px;
}
.history-industry {
background: #e3f2fd;
color: #1976d2;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 500;
}
.history-profession {
background: #f3e5f5;
color: #7b1fa2;
padding: 4px 8px;
border-radius: 12px;
font-size: 0.8rem;
font-weight: 500;
}
.history-content {
margin-bottom: 15px;
}
.history-original,
.history-optimized {
margin-bottom: 15px;
}
.history-original h4,
.history-optimized h4 {
font-size: 1rem;
font-weight: 600;
color: #333;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 8px;
}
.history-original h4::before {
content: "📝";
font-size: 1.2rem;
}
.history-optimized h4::before {
content: "✨";
font-size: 1.2rem;
}
.history-original p,
.history-optimized p {
color: #666;
line-height: 1.6;
margin: 0;
background: white;
padding: 12px;
border-radius: 8px;
border-left: 4px solid #e1e5e9;
}
.history-optimized p {
border-left-color: #3B82F6;
background: #f8faff;
}
/* 标签区域 */
.history-tags {
display: flex;
gap: 8px;
margin-bottom: 15px;
flex-wrap: wrap;
}
.tag {
background: linear-gradient(135deg, #6366F1, #8B5CF6);
color: white;
padding: 4px 12px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 500;
}
/* 评分区域 */
.history-rating {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 15px;
}
.stars {
display: flex;
gap: 2px;
}
.stars i {
color: #ddd;
font-size: 1.1rem;
transition: color 0.2s ease;
}
.stars i.active {
color: #ffc107;
}
/* 操作按钮 */
.history-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
flex-wrap: wrap;
}
.action-btn {
padding: 8px 16px;
border: none;
border-radius: 8px;
font-size: 0.9rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 6px;
}
.copy-btn {
background: #10B981;
color: white;
}
.copy-btn:hover {
background: #059669;
transform: translateY(-1px);
}
.favorite-btn {
background: #EF4444;
color: white;
}
.favorite-btn:hover {
background: #DC2626;
transform: translateY(-1px);
}
.favorite-btn.favorited {
background: #F59E0B;
}
.favorite-btn.favorited:hover {
background: #D97706;
}
.delete-btn {
background: #6B7280;
color: white;
}
.delete-btn:hover {
background: #4B5563;
transform: translateY(-1px);
}
/* 分页 */
.pagination {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 30px;
flex-wrap: wrap;
}
.pagination button {
padding: 10px 16px;
border: 2px solid #e1e5e9;
background: white;
color: #333;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 500;
}
.pagination button:hover {
border-color: #3B82F6;
color: #3B82F6;
}
.pagination button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination .current-page {
background: #3B82F6;
color: white;
border-color: #3B82F6;
}
/* 加载状态 */
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.loading-spinner {
display: inline-block;
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3B82F6;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 空状态 */
.no-history {
text-align: center;
padding: 60px 20px;
color: #666;
}
.no-history i {
font-size: 4rem;
color: #ddd;
margin-bottom: 20px;
}
.no-history h3 {
font-size: 1.5rem;
margin-bottom: 10px;
color: #333;
}
.no-history p {
font-size: 1rem;
color: #666;
}
/* 统计卡片 */
.stats-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: linear-gradient(135deg, #3B82F6, #1E3A8A);
color: white;
padding: 20px;
border-radius: 15px;
text-align: center;
box-shadow: 0 5px 15px rgba(59, 130, 246, 0.2);
}
.stat-card h3 {
font-size: 2rem;
margin-bottom: 5px;
font-weight: 700;
}
.stat-card p {
font-size: 0.9rem;
opacity: 0.9;
margin: 0;
}
/* 响应式设计 */
@media (max-width: 768px) {
.history-container {
padding: 10px;
}
.history-content {
padding: 20px;
}
.search-filters {
grid-template-columns: 1fr;
}
.history-header {
flex-direction: column;
gap: 10px;
}
.history-meta {
flex-direction: column;
align-items: flex-start;
gap: 8px;
}
.history-actions {
justify-content: flex-start;
}
.stats-cards {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 480px) {
.stats-cards {
grid-template-columns: 1fr;
}
.history-actions {
flex-direction: column;
}
.action-btn {
justify-content: center;
}
}
</style>
{% endblock %}
{% block content %}
<div class="history-container">
<div class="history-header">
<h1><i class="fas fa-history"></i> 优化历史</h1>
<p>查看和管理您的AI优化历史记录随时回顾精彩内容</p>
</div>
<div class="history-content">
<!-- 统计卡片 -->
<div class="stats-cards" id="statsCards">
<div class="stat-card">
<h3 id="totalCount">0</h3>
<p>总记录数</p>
</div>
<div class="stat-card">
<h3 id="todayCount">0</h3>
<p>今日生成</p>
</div>
<div class="stat-card">
<h3 id="avgRating">0</h3>
<p>平均评分</p>
</div>
<div class="stat-card">
<h3 id="timeSaved">0</h3>
<p>节省时间(分钟)</p>
</div>
</div>
<!-- 搜索和筛选 -->
<div class="search-filters">
<input type="text" id="searchInput" placeholder="搜索历史记录...">
<select id="typeFilter">
<option value="">所有类型</option>
<option value="提示词优化">提示词优化</option>
<option value="产品文案">产品文案</option>
<option value="商务邮件">商务邮件</option>
<option value="技术文档">技术文档</option>
<option value="营销文案">营销文案</option>
</select>
<select id="dateFilter">
<option value="">所有时间</option>
<option value="today">今天</option>
<option value="week">本周</option>
<option value="month">本月</option>
</select>
<select id="ratingFilter">
<option value="">所有评分</option>
<option value="5">5星</option>
<option value="4">4星</option>
<option value="3">3星</option>
<option value="2">2星</option>
<option value="1">1星</option>
</select>
<button id="searchBtn" class="search-btn">
<i class="fas fa-search"></i> 搜索
</button>
</div>
<!-- 历史记录列表 -->
<div id="historyList" class="history-list">
<div class="loading">
<div class="loading-spinner"></div>
<p>正在加载历史记录...</p>
</div>
</div>
<!-- 分页 -->
<div id="pagination" class="pagination" style="display: none;">
<!-- 分页内容 -->
</div>
</div>
</div>
<!-- 历史记录详情模态框 -->
<div class="modal fade" id="historyDetailModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-history"></i> 历史记录详情
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div id="history-detail-content">
<!-- 详情内容 -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">关闭</button>
<button type="button" class="btn btn-primary" id="copy-detail-btn">
<i class="fas fa-copy"></i> 复制结果
</button>
<button type="button" class="btn btn-warning" id="rate-detail-btn">
<i class="fas fa-star"></i> 评分
</button>
<button type="button" class="btn btn-danger" id="delete-detail-btn">
<i class="fas fa-trash"></i> 删除
</button>
</div>
</div>
</div>
</div>
<!-- 评分模态框 -->
<div class="modal fade" id="ratingModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fas fa-star"></i> 满意度评分
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="text-center">
<p class="mb-3">请为这次优化结果评分:</p>
<div class="rating-stars" id="ratingStars">
<i class="fas fa-star" data-rating="1"></i>
<i class="fas fa-star" data-rating="2"></i>
<i class="fas fa-star" data-rating="3"></i>
<i class="fas fa-star" data-rating="4"></i>
<i class="fas fa-star" data-rating="5"></i>
</div>
<p class="mt-3 text-muted" id="ratingText">点击星星进行评分</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="save-rating-btn" disabled>保存评分</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/optimization_history_db.js') }}"></script>
<script>
let currentPage = 1;
let currentHistoryId = null;
let currentRating = 0;
// 页面加载完成后执行
document.addEventListener('DOMContentLoaded', function() {
console.log('🚀 优化历史页面开始初始化...');
try {
// 初始化优化历史数据库对象
window.optimizationHistoryDB = new OptimizationHistoryDB();
console.log('✅ OptimizationHistoryDB对象创建成功');
loadStats();
console.log('✅ 统计信息加载完成');
loadHistory();
console.log('✅ 历史记录加载完成');
} catch (error) {
console.error('❌ 初始化失败:', error);
document.getElementById('historyList').innerHTML = `
<div class="error-message">
<h3>初始化失败</h3>
<p>错误信息: ${error.message}</p>
</div>
`;
}
// 搜索按钮点击事件
document.getElementById('searchBtn').addEventListener('click', function() {
currentPage = 1;
loadHistory();
});
// 回车搜索
document.getElementById('searchInput').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
currentPage = 1;
loadHistory();
}
});
// 筛选器变化
['typeFilter', 'dateFilter', 'ratingFilter'].forEach(id => {
document.getElementById(id).addEventListener('change', function() {
currentPage = 1;
loadHistory();
});
});
// 评分星星点击事件
document.querySelectorAll('#ratingStars i').forEach(star => {
star.addEventListener('click', function() {
currentRating = parseInt(this.dataset.rating);
updateRatingDisplay();
});
star.addEventListener('mouseenter', function() {
const rating = parseInt(this.dataset.rating);
highlightStars(rating);
});
});
document.getElementById('ratingStars').addEventListener('mouseleave', function() {
highlightStars(currentRating);
});
// 保存评分
document.getElementById('save-rating-btn').addEventListener('click', function() {
if (currentRating > 0 && currentHistoryId) {
optimizationHistoryDB.updateRating(currentHistoryId, currentRating)
.then(success => {
if (success) {
$('#ratingModal').modal('hide');
loadHistory();
loadStats();
}
});
}
});
// 复制详情
document.getElementById('copy-detail-btn').addEventListener('click', function() {
const optimizedText = document.querySelector('#history-detail-content .optimized-text').textContent;
navigator.clipboard.writeText(optimizedText).then(() => {
showToast('内容已复制到剪贴板', 'success');
});
});
// 删除详情
document.getElementById('delete-detail-btn').addEventListener('click', function() {
if (confirm('确定要删除这条历史记录吗?')) {
optimizationHistoryDB.deleteHistory(currentHistoryId)
.then(success => {
if (success) {
$('#historyDetailModal').modal('hide');
loadHistory();
loadStats();
}
});
}
});
});
// 加载统计信息
function loadStats() {
optimizationHistoryDB.getUserStats(30).then(stats => {
if (stats) {
document.getElementById('totalCount').textContent = stats.period.total_generations || 0;
document.getElementById('todayCount').textContent = stats.today.generation_count || 0;
document.getElementById('avgRating').textContent = stats.period.avg_rating || 0;
document.getElementById('timeSaved').textContent = stats.period.total_time_saved || 0;
}
});
}
// 加载历史记录
function loadHistory() {
console.log('🔄 开始加载历史记录...');
const search = document.getElementById('searchInput').value;
const typeFilter = document.getElementById('typeFilter').value;
const dateFilter = document.getElementById('dateFilter').value;
const ratingFilter = document.getElementById('ratingFilter').value;
console.log('📋 搜索参数:', { search, typeFilter, dateFilter, ratingFilter });
optimizationHistoryDB.getHistory({
page: currentPage,
search: search,
typeFilter: typeFilter,
dateFilter: dateFilter,
ratingFilter: ratingFilter
});
}
// 查看详情
function viewDetail(historyId) {
currentHistoryId = historyId;
// 这里需要从当前显示的历史记录中获取数据
// 实际实现中应该通过API获取完整数据
const historyItem = document.querySelector(`[data-id="${historyId}"]`);
if (historyItem) {
const originalText = historyItem.querySelector('.history-original p').textContent;
const optimizedText = historyItem.querySelector('.history-optimized p').textContent;
const type = historyItem.querySelector('.history-type').textContent;
const time = historyItem.querySelector('.history-time').textContent;
let html = `
<div class="row">
<div class="col-md-6">
<h6><i class="fas fa-edit"></i> 原始输入</h6>
<div class="border rounded p-3 bg-light original-text">
${escapeHtml(originalText)}
</div>
</div>
<div class="col-md-6">
<h6><i class="fas fa-magic"></i> 优化结果</h6>
<div class="border rounded p-3 bg-light optimized-text">
<pre class="mb-0">${escapeHtml(optimizedText)}</pre>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6">
<strong>类型:</strong> ${type}
</div>
<div class="col-md-6">
<strong>时间:</strong> ${time}
</div>
</div>
`;
document.getElementById('history-detail-content').innerHTML = html;
$('#historyDetailModal').modal('show');
}
}
// 评分功能
function rateHistory(historyId) {
currentHistoryId = historyId;
currentRating = 0;
updateRatingDisplay();
$('#ratingModal').modal('show');
}
// 更新评分显示
function updateRatingDisplay() {
highlightStars(currentRating);
document.getElementById('save-rating-btn').disabled = currentRating === 0;
const texts = ['', '很差', '一般', '还行', '很好', '优秀'];
document.getElementById('ratingText').textContent = texts[currentRating] || '点击星星进行评分';
}
// 高亮星星
function highlightStars(rating) {
document.querySelectorAll('#ratingStars i').forEach((star, index) => {
if (index < rating) {
star.classList.add('active');
} else {
star.classList.remove('active');
}
});
}
// 跳转到指定页面
function goToPage(page) {
currentPage = page;
loadHistory();
}
// 工具函数
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function showToast(message, type) {
// 使用优化历史DB类中的消息显示功能
if (window.optimizationHistoryDB) {
if (type === 'success') {
window.optimizationHistoryDB.showSuccess(message);
} else {
window.optimizationHistoryDB.showError(message);
}
} else {
alert(message);
}
}
</script>
{% endblock %}