feat(ai): move history page to v2 shell
All checks were successful
CD Pipeline / deploy (push) Successful in 2m19s
All checks were successful
CD Pipeline / deploy (push) Successful in 2m19s
This commit is contained in:
4
app.py
4
app.py
@@ -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 防護函數
|
||||
|
||||
@@ -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 # 用於模板顯示
|
||||
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user