feat(ai): move history page to v2 shell
All checks were successful
CD Pipeline / deploy (push) Successful in 2m19s

This commit is contained in:
OoO
2026-05-01 21:06:17 +08:00
parent 939ed5eef5
commit 9b3e0a4565
4 changed files with 157 additions and 28 deletions

4
app.py
View File

@@ -95,8 +95,8 @@ except Exception as e:
sys_log.error(f"無法檢測磁碟空間: {e}")
# 🚩 系統版本定義 (備份與顯示用)
# 🚩 2026-05-01 V10.73: Move AI intelligence center onto V2 shell
SYSTEM_VERSION = "V10.73"
# 🚩 2026-05-01 V10.74: Move AI history page onto V2 shell
SYSTEM_VERSION = "V10.74"
# ==========================================
# 🔒 SQL Injection 防護函數

View File

@@ -254,7 +254,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.73"
SYSTEM_VERSION = "V10.74"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -1,26 +1,23 @@
{% extends 'base.html' %}
{% extends 'ewoooc_base.html' %}
{% block title %}AI 生成歷史 - WOOO TECH{% endblock %}
{% block content %}
<div class="container-fluid py-4">
{% block ewooo_content %}
<div class="ai-history-page">
<!-- 頁面標題 -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2">
<div>
<h2 class="mb-1">
<i class="fas fa-history text-primary me-2"></i>AI 生成歷史
</h2>
<p class="text-muted mb-0">查看、管理和複用之前生成的文案</p>
</div>
<div class="d-flex gap-2">
<a href="/ai_recommend" class="btn btn-primary">
<i class="fas fa-magic me-1"></i>生成新文案
</a>
</div>
</div>
<section class="ai-history-hero">
<div>
<h1 class="ai-history-title">
<i class="fas fa-history"></i>
AI 生成歷史
</h1>
<p class="ai-history-subtitle">查看、管理和複用資料庫內已生成的文案與操作紀錄。</p>
</div>
</div>
<div class="ai-history-actions">
<a href="/ai_recommend" class="btn btn-primary ai-history-action-btn">
<i class="fas fa-magic me-1"></i>生成新文案
</a>
</div>
</section>
<!-- 統計卡片 -->
<div class="row mb-4" id="statsRow">
@@ -87,7 +84,7 @@
</div>
<!-- 篩選列 -->
<div class="card shadow-sm mb-4">
<div class="card shadow-sm mb-4 ai-history-panel">
<div class="card-body py-3">
<div class="row g-3 align-items-end">
<div class="col-md-3">
@@ -239,23 +236,121 @@
</div>
</div>
{% endblock %}
{% block extra_css %}
<style>
.ai-history-page {
display: flex;
flex-direction: column;
gap: 18px;
}
.ai-history-hero {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 16px;
align-items: center;
padding: 22px;
border: 1px solid var(--momo-border-strong);
border-radius: 8px;
background:
radial-gradient(circle at 18px 18px, rgba(42, 37, 32, 0.12) 1px, transparent 1px),
linear-gradient(135deg, rgba(242, 178, 90, 0.22), rgba(255, 255, 255, 0.94) 46%, rgba(42, 37, 32, 0.06));
background-size: 18px 18px, auto;
box-shadow: var(--momo-shadow-soft);
}
.ai-history-title {
display: flex;
align-items: center;
gap: 10px;
margin: 0;
color: var(--momo-text-strong);
font-family: var(--momo-font-display);
font-size: clamp(1.45rem, 2vw, 2.05rem);
font-weight: 800;
letter-spacing: 0;
}
.ai-history-title i {
color: var(--momo-warm-caramel);
}
.ai-history-subtitle {
margin: 8px 0 0;
color: var(--momo-text-muted);
}
.ai-history-actions {
display: flex;
justify-content: flex-end;
}
.ai-history-action-btn {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 40px;
border-radius: 8px;
background: var(--momo-text-strong);
border-color: var(--momo-text-strong);
font-weight: 800;
}
.ai-history-page #statsRow .card,
.ai-history-panel,
.history-card {
border: 1px solid var(--momo-border-subtle) !important;
border-radius: 8px;
background: rgba(255, 255, 255, 0.84);
box-shadow: var(--momo-shadow-soft);
}
.ai-history-page #statsRow .card-body {
min-height: 112px;
}
.ai-history-page #statsRow h3 {
color: var(--momo-text-strong);
font-family: var(--momo-font-mono);
font-weight: 800;
}
.ai-history-page #statsRow .small,
.ai-history-page .form-label,
.ai-history-page .text-muted {
color: var(--momo-text-muted) !important;
}
.ai-history-panel .input-group-text,
.ai-history-panel .form-control,
.ai-history-panel .form-select {
border-color: var(--momo-border-subtle);
border-radius: 8px;
}
.ai-history-panel .input-group-text {
background: rgba(250, 247, 240, 0.74) !important;
}
.history-card {
transition: all 0.2s ease;
border: 1px solid #e9ecef;
}
.history-card:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
box-shadow: 0 10px 26px rgba(42, 37, 32, 0.1);
transform: translateY(-2px);
}
.history-card.selected {
border-color: #0d6efd;
background-color: #f8f9ff;
border-color: rgba(172, 92, 58, 0.42) !important;
background: rgba(255, 247, 235, 0.9);
}
.copy-content {
max-height: 120px;
overflow: hidden;
position: relative;
border: 1px solid rgba(42, 37, 32, 0.08);
background: rgba(250, 247, 240, 0.74) !important;
}
.copy-content::after {
content: '';
@@ -264,7 +359,7 @@
left: 0;
right: 0;
height: 30px;
background: linear-gradient(transparent, white);
background: linear-gradient(transparent, rgba(250, 247, 240, 0.96));
}
.rating-stars i {
color: #dee2e6;
@@ -285,6 +380,17 @@
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
}
@media (max-width: 768px) {
.ai-history-hero {
grid-template-columns: 1fr;
}
.ai-history-actions,
.ai-history-action-btn {
width: 100%;
}
}
</style>
{% endblock %}

View File

@@ -178,6 +178,29 @@ def test_ai_intelligence_uses_v2_shell_and_real_runtime_apis():
assert "ai_price_recommendations" in route_source
def test_ai_history_uses_v2_shell_and_real_history_apis():
template = (ROOT / "templates/ai_history.html").read_text(encoding="utf-8")
route_source = (ROOT / "routes/ai_routes.py").read_text(encoding="utf-8")
assert "{% extends 'ewoooc_base.html' %}" in template
assert "{% block ewooo_content %}" in template
assert "{% block extra_css %}" in template
assert "ai-history-hero" in template
assert "ai-history-panel" in template
assert "fetch('/api/ai/statistics?days=30')" in template
assert "fetch(`/api/ai/history?${params}`)" in template
assert "fetch(`/api/ai/history/${id}`" in template
assert "fetch('/api/ai/history/batch'" in template
assert "mock" not in template.lower()
assert "假商品" not in template
assert "@ai_bp.route('/ai_history')" in route_source
assert "render_template('ai_history.html')" in route_source
assert "@ai_bp.route('/api/ai/history')" in route_source
assert "ai_history_service.get_history_list" in route_source
assert "ai_history_service.get_statistics" in route_source
def test_dashboard_v2_restores_real_price_history_chart():
route_source = (ROOT / "routes/api_routes.py").read_text(encoding="utf-8")
dashboard = (ROOT / "templates/dashboard_v2.html").read_text(encoding="utf-8")