feat: add consistent footer navigation across all pages

- Footer on homepage, API docs, and application pages
- Two groups: 专家工具 + API 服务
- Cleaned up homepage header to show only 5号/6号 experts
- Responsive layout for mobile

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-28 00:31:05 +08:00
parent e85b812f0d
commit 6f80d7f4c0
7 changed files with 1856 additions and 533 deletions

View File

@@ -152,6 +152,22 @@ console.log(data.optimized_prompt);</code></pre>
</table>
</div>
</div>
<div class="site-footer">
<div class="footer-group">
<span class="footer-label">专家工具</span>
<a href="/">AI 提示词生成器</a>
<a href="/expert-generate-6">6号专家纯提示词</a>
<a href="/expert-generate-5">5号专家含样例</a>
</div>
<div class="footer-divider"></div>
<div class="footer-group">
<span class="footer-label">API 服务</span>
<a href="/api-docs">API 文档</a>
<a href="/api-key/apply">申请 API Key</a>
<a href="/admin/apikeys">管理 API Key</a>
</div>
</div>
</div>
{% endblock %}
@@ -198,6 +214,20 @@ console.log(data.optimized_prompt);</code></pre>
@media (max-width: 768px) {
.docs-layout { padding: 1rem; }
.endpoint-card { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
.site-footer { flex-direction: column; gap: 1rem; }
.footer-divider { width: auto; height: 1px; }
}
/* Footer nav */
.site-footer {
display: flex; justify-content: center; align-items: flex-start;
gap: 2rem; margin-top: 3rem; padding-top: 1.5rem;
border-top: 1px solid var(--border-color);
}
.footer-group { display: flex; flex-direction: column; gap: 0.35rem; }
.footer-label { font-size: 0.72rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.25rem; }
.footer-group a { font-size: 0.85rem; color: var(--text-secondary); text-decoration: none; transition: color 0.2s; }
.footer-group a:hover { color: #06b6d4; }
.footer-divider { width: 1px; background: var(--border-color); align-self: stretch; }
</style>
{% endblock %}

View File

@@ -63,6 +63,22 @@
</div>
</div>
</div>
<div class="site-footer">
<div class="footer-group">
<span class="footer-label">专家工具</span>
<a href="/">AI 提示词生成器</a>
<a href="/expert-generate-6">6号专家纯提示词</a>
<a href="/expert-generate-5">5号专家含样例</a>
</div>
<div class="footer-divider"></div>
<div class="footer-group">
<span class="footer-label">API 服务</span>
<a href="/api-docs">API 文档</a>
<a href="/api-key/apply">申请 API Key</a>
<a href="/admin/apikeys">管理 API Key</a>
</div>
</div>
</div>
</div>
@@ -200,9 +216,23 @@ button[type="submit"]:disabled { opacity: 0.6; cursor: not-allowed; transform: n
.toast.success { background: #10b981; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
/* Footer nav */
.site-footer {
display: flex; justify-content: center; align-items: flex-start;
gap: 2rem; margin-top: 3rem; padding-top: 1.5rem;
border-top: 1px solid var(--border-color);
}
.footer-group { display: flex; flex-direction: column; gap: 0.35rem; }
.footer-label { font-size: 0.72rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.25rem; }
.footer-group a { font-size: 0.85rem; color: var(--text-secondary); text-decoration: none; transition: color 0.2s; }
.footer-group a:hover { color: #06b6d4; }
.footer-divider { width: 1px; background: var(--border-color); align-self: stretch; }
@media (max-width: 768px) {
.layout { padding: 1.5rem 1rem; }
.card { padding: 1.5rem 1.25rem; }
.site-footer { flex-direction: column; gap: 1rem; }
.footer-divider { width: auto; height: 1px; }
}
</style>
{% endblock %}

View File

@@ -1,250 +1,662 @@
{% extends "base.html" %}
{% block title %}专家模式{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>{{ page_title | default('专家提示词生成器') }}</h2>
<a href="{{ url_for('main.index') }}" class="btn btn-outline-primary">
<i class="fas fa-arrow-left"></i> 返回基础模式
</a>
<div class="expert-layout">
<div class="expert-container">
<div class="page-header">
<h1><i class="fas fa-brain"></i> 专家提示词生成器</h1>
<p class="subtitle">无需选择模板描述您的需求AI 自动识别意图并生成专业提示词</p>
</div>
<div class="expert-card">
<div class="flow-steps">
<div class="step">
<span class="step-num">1</span>
<span class="step-label">意图分析</span>
</div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step">
<span class="step-num">2</span>
<span class="step-label">专家匹配</span>
</div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step">
<span class="step-num">3</span>
<span class="step-label">生成提示词</span>
</div>
</div>
<div class="card">
<div class="card-body">
<form id="expertPromptForm">
<div class="mb-3">
<label for="input_text" class="form-label">请描述您的需求</label>
<textarea class="form-control" id="input_text" name="input_text" rows="4" required></textarea>
<div class="form-text">详细描述您的需求,系统将进行专业分析并生成高质量提示词</div>
<form id="expertForm">
<div class="input-group">
<label for="inputText">请描述您的需求</label>
<textarea id="inputText" rows="5"
placeholder="例如:我需要写一份关于新产品发布的营销文案,目标受众是年轻白领..."></textarea>
<span class="input-hint">描述越详细,意图识别越准确,生成的提示词质量越高</span>
</div>
<button type="submit" id="generateBtn">
<i class="fas fa-magic"></i> 开始生成
</button>
</form>
</div>
<!-- 加载状态 -->
<div id="loadingArea" style="display:none;">
<div class="loading-card">
<div class="loading-spinner"></div>
<p class="loading-text" id="loadingText">正在分析您的需求...</p>
<div class="loading-steps">
<span class="load-step active">识别意图</span>
<span class="load-step">匹配专家</span>
<span class="load-step">生成提示词</span>
</div>
</div>
</div>
<!-- 结果区域 -->
<div id="resultArea" style="display:none;">
<div class="result-card">
<div class="result-section">
<h3><i class="fas fa-search"></i> 需求分析</h3>
<div class="analysis-grid">
<div class="analysis-item">
<span class="analysis-label">核心意图</span>
<span class="analysis-value" id="coreIntent"></span>
</div>
<button type="submit" class="btn btn-primary" id="generateBtn">
<i class="fas fa-magic"></i> 生成专家提示词
<div class="analysis-item">
<span class="analysis-label">专业领域</span>
<span class="analysis-value" id="domain"></span>
</div>
<div class="analysis-item">
<span class="analysis-label">预期输出</span>
<span class="analysis-value" id="expectedOutput"></span>
</div>
</div>
<div class="analysis-list">
<div class="analysis-list-item">
<h4>关键需求</h4>
<ul id="keyRequirements"></ul>
</div>
<div class="analysis-list-item">
<h4>约束条件</h4>
<ul id="constraints"></ul>
</div>
</div>
</div>
<div class="result-section">
<div class="result-header">
<h3><i class="fas fa-star"></i> 生成的专家提示词</h3>
<button class="btn-copy" id="copyBtn">
<i class="fas fa-copy"></i> 复制
</button>
</form>
</div>
</div>
<div id="loadingIndicator" class="text-center mt-4 d-none">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">生成中...</span>
</div>
<p class="mt-2">正在分析需求并生成专业提示词...</p>
</div>
<div id="resultCard" class="card mt-4 d-none">
<div class="card-body">
<h5 class="card-title mb-4">生成结果</h5>
<div class="mb-4">
<h6 class="mb-3">需求分析</h6>
<div id="intentAnalysis" class="border rounded p-3 bg-light">
<div class="row">
<div class="col-md-6">
<p><strong>核心意图:</strong><span id="coreIntent"></span></p>
<p><strong>专业领域:</strong><span id="domain"></span></p>
</div>
<div class="col-md-6">
<p><strong>预期输出:</strong><span id="expectedOutput"></span></p>
</div>
</div>
<div class="mt-2">
<p><strong>关键需求:</strong></p>
<ul id="keyRequirements" class="mb-0"></ul>
</div>
<div class="mt-2">
<p><strong>约束条件:</strong></p>
<ul id="constraints" class="mb-0"></ul>
</div>
</div>
</div>
<div>
<h6 class="mb-3">生成的专家提示词</h6>
<div id="generatedPrompt" class="border rounded p-3"></div>
<div class="mt-3">
<button class="btn btn-sm btn-primary copy-btn" data-target="generatedPrompt">
<i class="fas fa-copy"></i> 复制提示词
</button>
</div>
</div>
<div class="result-content" id="generatedPrompt"></div>
</div>
</div>
</div>
</div>
</div>
{% block scripts %}
<script>
document.getElementById('expertPromptForm').addEventListener('submit', async function(e) {
e.preventDefault();
const input_text = document.getElementById('input_text').value;
const loadingIndicator = document.getElementById('loadingIndicator');
const resultCard = document.getElementById('resultCard');
const generateBtn = document.getElementById('generateBtn');
// 显示加载指示器
loadingIndicator.classList.remove('d-none');
generateBtn.disabled = true;
resultCard.classList.add('d-none');
try {
const response = await fetch('/api/wx/generate/expert', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
input_text: input_text,
uid: '12345' // 这里应该使用实际的用户ID
})
});
const data = await response.json();
if (data.code === 200) {
// 更新意图分析结果
document.getElementById('coreIntent').textContent = data.data.intent_analysis.core_intent;
document.getElementById('domain').textContent = data.data.intent_analysis.domain;
document.getElementById('expectedOutput').textContent = data.data.intent_analysis.expected_output;
// 更新关键需求列表
const keyRequirementsList = document.getElementById('keyRequirements');
keyRequirementsList.innerHTML = data.data.intent_analysis.key_requirements
.map(req => `<li>${req}</li>`).join('');
// 更新约束条件列表
const constraintsList = document.getElementById('constraints');
constraintsList.innerHTML = data.data.intent_analysis.constraints
.map(constraint => `<li>${constraint}</li>`).join('');
// 更新生成的提示词
document.getElementById('generatedPrompt').textContent = data.data.generated_prompt;
// 显示结果卡片
resultCard.classList.remove('d-none');
} else {
alert('生成失败:' + data.message);
}
} catch (error) {
console.error('Error:', error);
alert('请求失败,请稍后重试');
} finally {
// 隐藏加载指示器并启用按钮
loadingIndicator.classList.add('d-none');
generateBtn.disabled = false;
}
});
// 改进的复制功能实现
function copyToClipboard(text) {
// 创建临时文本区域
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
try {
// 选择文本
textArea.select();
textArea.setSelectionRange(0, 99999); // 对于移动设备
// 尝试使用新的 API
if (navigator.clipboard && window.isSecureContext) {
// 对于现代浏览器
navigator.clipboard.writeText(text).then(() => {
showCopySuccess();
}).catch(err => {
console.error('复制失败:', err);
// 回退到传统方法
document.execCommand('copy');
showCopySuccess();
});
} else {
// 对于不支持 Clipboard API 的浏览器
const successful = document.execCommand('copy');
if (successful) {
showCopySuccess();
} else {
console.error('复制失败');
}
}
} catch (err) {
console.error('复制出错:', err);
} finally {
// 清理临时元素
document.body.removeChild(textArea);
}
}
// 显示复制成功的反馈
function showCopySuccess() {
const copyBtn = document.querySelector('.copy-btn');
const originalHtml = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fas fa-check"></i> 已复制';
copyBtn.classList.add('copied');
setTimeout(() => {
copyBtn.innerHTML = originalHtml;
copyBtn.classList.remove('copied');
}, 2000);
}
// 绑定复制按钮点击事件
document.addEventListener('DOMContentLoaded', function() {
const copyBtn = document.querySelector('.copy-btn');
copyBtn.addEventListener('click', function() {
const targetId = this.dataset.target;
const text = document.getElementById(targetId).textContent;
copyToClipboard(text);
});
});
</script>
<!-- Toast -->
<div class="toast-container" id="toastContainer"></div>
{% endblock %}
{% block extra_css %}
<style>
/* 添加复制按钮样式 */
.copy-btn {
padding: 8px 16px;
background-color: #4a90e2;
.expert-layout {
min-height: calc(100vh - 120px);
background: linear-gradient(135deg, #f8fafc 0%, #eff6ff 100%);
padding: 3rem 1.5rem;
}
.expert-container {
max-width: 800px;
margin: 0 auto;
}
.page-header {
text-align: center;
margin-bottom: 2.5rem;
}
.page-header h1 {
font-size: 2rem;
font-weight: 700;
color: var(--text-color);
margin-bottom: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
}
.page-header h1 i {
background: var(--gradient-primary);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.page-header .subtitle {
color: var(--text-light);
font-size: 1rem;
}
/* ====== 流程步骤 ====== */
.flow-steps {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
margin-bottom: 2rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.step {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.35rem;
}
.step-num {
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--gradient-primary);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.9rem;
}
.step-label {
font-size: 0.8rem;
color: var(--text-light);
font-weight: 500;
}
.step-arrow {
color: var(--text-muted);
font-size: 0.85rem;
margin-top: -14px;
}
/* ====== 表单 ====== */
.expert-card {
background: white;
border-radius: 16px;
padding: 2rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
}
.input-group {
margin-bottom: 1.5rem;
}
.input-group label {
display: block;
font-weight: 600;
font-size: 0.95rem;
margin-bottom: 0.5rem;
color: var(--text-color);
}
.input-group textarea {
width: 100%;
padding: 1rem;
border: 2px solid var(--border-color);
border-radius: 12px;
font-size: 0.95rem;
font-family: inherit;
line-height: 1.6;
resize: vertical;
min-height: 140px;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.input-group textarea:focus {
outline: none;
border-color: var(--primary-light);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
.input-hint {
display: block;
margin-top: 0.5rem;
font-size: 0.8rem;
color: var(--text-muted);
}
button[type="submit"] {
width: 100%;
padding: 0.875rem;
background: var(--gradient-primary);
color: white;
border: none;
border-radius: 4px;
border-radius: 12px;
font-size: 1.05rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.copy-btn:hover {
background-color: #357abd;
button[type="submit"]:hover {
transform: translateY(-1px);
box-shadow: 0 4px 16px rgba(99, 102, 241, 0.3);
}
.copy-btn.copied {
background-color: #45a049;
button[type="submit"]:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.copy-btn i {
margin-right: 6px;
/* ====== 加载状态 ====== */
.loading-card {
background: white;
border-radius: 16px;
padding: 3rem 2rem;
text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
margin-top: 1.5rem;
}
/* 生成的提示词样式 */
#generatedPrompt {
background-color: #f8f9fa;
padding: 1rem;
.loading-spinner {
width: 48px;
height: 48px;
border: 3px solid var(--border-color);
border-top-color: var(--primary-color);
border-radius: 50%;
margin: 0 auto 1.5rem;
animation: spin 0.7s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.loading-text {
font-size: 1.05rem;
color: var(--text-color);
font-weight: 500;
margin-bottom: 1.5rem;
}
.loading-steps {
display: flex;
gap: 1.5rem;
justify-content: center;
}
.load-step {
font-size: 0.8rem;
color: var(--text-muted);
padding: 0.35rem 0.75rem;
border-radius: 20px;
background: #f1f5f9;
transition: all 0.5s ease;
}
.load-step.active {
background: var(--gradient-primary);
color: white;
}
/* ====== 结果区域 ====== */
.result-card {
background: white;
border-radius: 16px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
margin-top: 1.5rem;
overflow: hidden;
animation: fadeUp 0.4s ease;
}
@keyframes fadeUp {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.result-section {
padding: 1.5rem 2rem;
}
.result-section + .result-section {
border-top: 1px solid var(--border-color);
}
.result-section h3 {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 1rem;
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--text-color);
}
.result-section h3 i {
color: var(--primary-color);
}
/* 分析网格 */
.analysis-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-bottom: 1.5rem;
}
.analysis-item {
background: #f8fafc;
border-radius: 10px;
padding: 1rem;
}
.analysis-label {
display: block;
font-size: 0.75rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 0.35rem;
}
.analysis-value {
font-weight: 600;
color: var(--text-color);
font-size: 0.95rem;
}
.analysis-list {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.analysis-list-item h4 {
font-size: 0.85rem;
font-weight: 600;
color: var(--text-secondary);
margin-bottom: 0.5rem;
}
.analysis-list-item ul {
margin: 0;
padding-left: 1.25rem;
}
.analysis-list-item li {
font-size: 0.875rem;
color: var(--text-color);
margin-bottom: 0.25rem;
}
/* 结果头部 */
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0;
}
.result-header h3 {
margin-bottom: 0;
}
.btn-copy {
display: inline-flex;
align-items: center;
gap: 0.4rem;
padding: 0.4rem 0.875rem;
border-radius: 6px;
border: 1px solid var(--border-color);
background: white;
cursor: pointer;
font-size: 0.85rem;
color: var(--text-secondary);
transition: all 0.2s ease;
white-space: nowrap;
}
.btn-copy:hover {
background: #f1f5f9;
color: var(--primary-color);
}
.btn-copy.copied {
background: #10b981;
color: white;
border-color: #10b981;
}
.result-content {
margin-top: 1rem;
background: #f8fafc;
border-radius: 10px;
padding: 1.25rem;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 0.9rem;
line-height: 1.7;
white-space: pre-wrap;
font-family: monospace;
line-height: 1.5;
word-break: break-word;
color: var(--text-color);
}
/* 加载指示器样式 */
#loadingIndicator {
margin: 2rem 0;
/* ====== Toast ====== */
.toast-container {
position: fixed;
top: 1rem;
right: 1rem;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.spinner-border {
width: 3rem;
height: 3rem;
.toast {
padding: 0.75rem 1.25rem;
border-radius: 8px;
color: white;
font-size: 0.875rem;
font-weight: 500;
animation: slideIn 0.3s ease;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.toast.error { background: #ef4444; }
.toast.success { background: #10b981; }
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
/* ====== 响应式 ====== */
@media (max-width: 768px) {
.expert-layout {
padding: 1.5rem 1rem;
}
.page-header h1 {
font-size: 1.5rem;
}
.expert-card {
padding: 1.25rem;
}
.analysis-grid {
grid-template-columns: 1fr;
}
.analysis-list {
grid-template-columns: 1fr;
}
.flow-steps {
gap: 0.5rem;
}
}
</style>
{% endblock %}
{% endblock %}
{% block scripts %}
<script>
(function() {
const form = document.getElementById('expertForm');
const textarea = document.getElementById('inputText');
const generateBtn = document.getElementById('generateBtn');
const loadingArea = document.getElementById('loadingArea');
const loadingText = document.getElementById('loadingText');
const resultArea = document.getElementById('resultArea');
const loadSteps = document.querySelectorAll('.load-step');
// ====== Toast ======
function showToast(message, type) {
const container = document.getElementById('toastContainer');
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.textContent = message;
container.appendChild(toast);
setTimeout(function() {
toast.style.opacity = '0';
toast.style.transform = 'translateX(100%)';
toast.style.transition = 'all 0.3s ease';
setTimeout(function() { toast.remove(); }, 300);
}, 3500);
}
// ====== 加载步骤动画 ======
function animateLoadStep(index) {
loadSteps.forEach(function(s, i) {
s.classList.toggle('active', i <= index);
});
}
// ====== 表单提交 ======
form.addEventListener('submit', async function(e) {
e.preventDefault();
var input = textarea.value.trim();
if (!input) {
showToast('请输入您的需求描述', 'error');
textarea.focus();
return;
}
if (input.length < 10) {
showToast('需求描述过于简短请至少输入10个字', 'error');
textarea.focus();
return;
}
// 显示加载状态
loadingArea.style.display = '';
resultArea.style.display = 'none';
generateBtn.disabled = true;
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 生成中...';
// 模拟阶段动画
animateLoadStep(0);
loadingText.textContent = '正在分析您的需求意图...';
var stepTimer1 = setTimeout(function() {
animateLoadStep(1);
loadingText.textContent = '匹配领域专家模板...';
}, 1000);
var stepTimer2 = setTimeout(function() {
animateLoadStep(2);
loadingText.textContent = '正在生成专业提示词...';
}, 2500);
try {
var response = await fetch('/api/wx/generate/expert', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input_text: input, uid: '1' })
});
var data = await response.json();
clearTimeout(stepTimer1);
clearTimeout(stepTimer2);
if (data.code === 200) {
// 更新分析结果
document.getElementById('coreIntent').textContent = data.data.intent_analysis.core_intent;
document.getElementById('domain').textContent = data.data.intent_analysis.domain;
document.getElementById('expectedOutput').textContent = data.data.intent_analysis.expected_output;
var reqList = document.getElementById('keyRequirements');
reqList.innerHTML = data.data.intent_analysis.key_requirements.map(function(r) { return '<li>' + r + '</li>'; }).join('');
var conList = document.getElementById('constraints');
conList.innerHTML = data.data.intent_analysis.constraints.map(function(c) { return '<li>' + c + '</li>'; }).join('');
// 更新生成的提示词
document.getElementById('generatedPrompt').textContent = data.data.generated_prompt;
loadingArea.style.display = 'none';
resultArea.style.display = '';
resultArea.scrollIntoView({ behavior: 'smooth', block: 'start' });
} else {
loadingArea.style.display = 'none';
showToast(data.message || '生成失败,请稍后重试', 'error');
}
} catch (err) {
console.error('Error:', err);
clearTimeout(stepTimer1);
clearTimeout(stepTimer2);
loadingArea.style.display = 'none';
showToast('网络请求失败,请检查网络连接', 'error');
} finally {
generateBtn.disabled = false;
generateBtn.innerHTML = '<i class="fas fa-magic"></i> 开始生成';
loadSteps.forEach(function(s) { s.classList.remove('active'); });
}
});
// ====== 复制功能 ======
document.getElementById('copyBtn').addEventListener('click', function() {
var text = document.getElementById('generatedPrompt').textContent;
if (!text) return;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function() {
showCopyFeedback();
}).catch(function() {
showToast('复制失败', 'error');
});
} else {
var ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed';
ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
showCopyFeedback();
}
});
function showCopyFeedback() {
var btn = document.getElementById('copyBtn');
btn.innerHTML = '<i class="fas fa-check"></i> 已复制';
btn.classList.add('copied');
setTimeout(function() {
btn.innerHTML = '<i class="fas fa-copy"></i> 复制';
btn.classList.remove('copied');
}, 2000);
}
})();
</script>
{% endblock %}

View File

@@ -1,161 +1,338 @@
{% extends "base.html" %}
{% block title %}智能提示词优化2号专家 - 提示词大师{% endblock %}
{% block title %}2号专家模板拼接{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2">
<h2>智能提示词优化2号专家</h2>
<div class="d-flex gap-2">
<a href="{{ url_for('history.history_page') }}" class="btn btn-outline-secondary"><i class="fas fa-history me-1"></i>查看历史</a>
<a href="{{ url_for('main.index') }}" class="btn btn-outline-primary">
<i class="fas fa-arrow-left"></i> 返回基础模式
</a>
<div class="expert-layout">
<div class="expert-container">
<div class="page-header">
<h1><i class="fas fa-bolt"></i> 提示词生成器 2号</h1>
<p class="subtitle">方向A · 单次AI调用 + 代码模板拼接 · 更快更省钱</p>
</div>
<div class="diff-badge">
<span class="badge-tag">vs 原始专家模式</span>
<span class="diff-detail">砍掉第二次 API 调用,意图分析结果直接用代码模板拼成结构化提示词</span>
</div>
<div class="expert-card">
<div class="flow-steps">
<div class="step">
<span class="step-num">1</span>
<span class="step-label">意图分析</span>
</div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step">
<span class="step-num">2</span>
<span class="step-label">模板拼接</span>
</div>
</div>
<div class="card">
<div class="card-body">
<form id="expertPromptForm">
<div class="mb-3">
<label for="input_text" class="form-label">请描述您的需求</label>
<textarea class="form-control" id="input_text" name="input_text" rows="4" required></textarea>
<div class="form-text">详细描述您的需求,系统将进行专业分析并生成高质量提示词</div>
<form id="expertForm">
<div class="input-group">
<label for="inputText">请描述您的需求</label>
<textarea id="inputText" rows="5"
placeholder="例如:我需要写一份关于新产品发布的营销文案,目标受众是年轻白领..."></textarea>
<span class="input-hint">仅 1 次 API 调用,响应更快且结果稳定可预测</span>
</div>
<button type="submit" id="generateBtn">
<i class="fas fa-magic"></i> 快速生成
</button>
</form>
</div>
<div id="loadingArea" style="display:none;">
<div class="loading-card">
<div class="loading-spinner"></div>
<p class="loading-text" id="loadingText">正在分析意图 + 构造提示词...</p>
<p class="loading-sub">仅 1 次 API 调用,即将完成</p>
</div>
</div>
<div id="resultArea" style="display:none;">
<div class="result-card">
<div class="result-section">
<h3><i class="fas fa-search"></i> 意图分析结果</h3>
<div class="analysis-grid">
<div class="analysis-item">
<span class="analysis-label">核心意图</span>
<span class="analysis-value" id="coreIntent"></span>
</div>
<button type="submit" class="btn btn-primary" id="generateBtn">
<i class="fas fa-magic"></i> 生成专家提示词
</button>
</form>
</div>
</div>
<div id="loadingIndicator" class="text-center mt-4 d-none">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">生成中...</span>
</div>
<p class="mt-2">正在分析需求并生成专业提示词...</p>
</div>
<div id="resultCard" class="card mt-4 d-none">
<div class="card-body">
<h5 class="card-title mb-4">生成结果</h5>
<div class="mb-4">
<h6 class="mb-3">需求分析</h6>
<div id="intentAnalysis" class="border rounded p-3 bg-light">
<div class="row">
<div class="col-md-6">
<p><strong>核心意图:</strong><span id="coreIntent"></span></p>
<p><strong>专业领域:</strong><span id="domain"></span></p>
</div>
<div class="col-md-6">
<p><strong>预期输出:</strong><span id="expectedOutput"></span></p>
</div>
</div>
<div class="mt-2">
<p><strong>关键需求:</strong></p>
<ul id="keyRequirements" class="mb-0"></ul>
</div>
<div class="mt-2">
<p><strong>约束条件:</strong></p>
<ul id="constraints" class="mb-0"></ul>
</div>
<div class="analysis-item">
<span class="analysis-label">专业领域</span>
<span class="analysis-value" id="domain"></span>
</div>
<div class="analysis-item">
<span class="analysis-label">预期输出</span>
<span class="analysis-value" id="expectedOutput"></span>
</div>
</div>
<div>
<h6 class="mb-3">生成的专家提示词</h6>
<div id="generatedPrompt" class="border rounded p-3"></div>
<div class="mt-3">
<button class="btn btn-sm btn-primary copy-btn" data-target="generatedPrompt">
<i class="fas fa-copy"></i> 复制提示词
<div class="analysis-list">
<div class="analysis-list-item">
<h4>关键需求</h4>
<ul id="keyRequirements"></ul>
</div>
<div class="analysis-list-item">
<h4>约束条件</h4>
<ul id="constraints"></ul>
</div>
</div>
</div>
<div class="result-section">
<div class="result-header">
<h3><i class="fas fa-file-code"></i> 模板拼接结果</h3>
<div class="result-header-right">
<span class="method-chip">仅 1 次 API</span>
<button class="btn-copy" id="copyBtn">
<i class="fas fa-copy"></i> 复制
</button>
</div>
</div>
<div class="result-content" id="generatedPrompt"></div>
</div>
</div>
</div>
</div>
</div>
{% block scripts %}
<script>
document.getElementById('expertPromptForm').addEventListener('submit', async function(e) {
e.preventDefault();
var form = e.target;
if (form.dataset.submitting === '1') return;
form.dataset.submitting = '1';
var generateBtn = document.getElementById('generateBtn');
generateBtn.disabled = true;
var input_text = document.getElementById('input_text').value.trim();
var loadingIndicator = document.getElementById('loadingIndicator');
var resultCard = document.getElementById('resultCard');
if (!input_text) { form.dataset.submitting = ''; generateBtn.disabled = false; alert('请输入您的需求'); return; }
loadingIndicator.classList.remove('d-none');
resultCard.classList.add('d-none');
try {
var response = await fetch('/api/expert-generate-2/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input_text: input_text })
});
var data = await response.json();
if (data.code === 200) {
document.getElementById('coreIntent').textContent = data.data.intent_analysis.core_intent || '';
document.getElementById('domain').textContent = data.data.intent_analysis.domain || '';
document.getElementById('expectedOutput').textContent = data.data.intent_analysis.expected_output || '';
document.getElementById('keyRequirements').innerHTML = (data.data.intent_analysis.key_requirements || [])
.map(function(r){ return '<li>'+r+'</li>'; }).join('');
document.getElementById('constraints').innerHTML = (data.data.intent_analysis.constraints || [])
.map(function(c){ return '<li>'+c+'</li>'; }).join('');
document.getElementById('generatedPrompt').textContent = data.data.generated_prompt || '';
resultCard.classList.remove('d-none');
} else {
alert('生成失败:' + (data.message || '请重试'));
}
} catch (err) {
console.error(err);
alert('请求失败,请稍后重试');
} finally {
loadingIndicator.classList.add('d-none');
generateBtn.disabled = false;
form.dataset.submitting = '';
}
});
function copyToClipboard(text) {
var ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
ta.setSelectionRange(0, 99999);
try {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(showCopySuccess).catch(function(){ document.execCommand('copy'); showCopySuccess(); });
} else { document.execCommand('copy'); showCopySuccess(); }
} catch(e){}
document.body.removeChild(ta);
}
function showCopySuccess() {
var btn = document.querySelector('.copy-btn');
if (!btn) return;
var orig = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-check"></i> 已复制';
btn.classList.add('copied');
setTimeout(function(){ btn.innerHTML = orig; btn.classList.remove('copied'); }, 2000);
}
document.addEventListener('DOMContentLoaded', function() {
var copyBtn = document.querySelector('.copy-btn');
if (copyBtn) copyBtn.addEventListener('click', function() {
var text = document.getElementById(this.dataset.target).textContent;
copyToClipboard(text);
});
});
</script>
<div class="toast-container" id="toastContainer"></div>
{% endblock %}
{% block extra_css %}
<style>
.copy-btn { padding: 8px 16px; background-color: #4a90e2; color: white; border: none; border-radius: 4px; cursor: pointer; }
.copy-btn:hover { background-color: #357abd; }
.copy-btn.copied { background-color: #45a049; }
#generatedPrompt { background-color: #f8f9fa; padding: 1rem; white-space: pre-wrap; font-family: monospace; line-height: 1.5; }
#loadingIndicator { margin: 2rem 0; }
.spinner-border { width: 3rem; height: 3rem; }
.expert-layout {
min-height: calc(100vh - 120px);
background: linear-gradient(135deg, #f0fdf4 0%, #ecfeff 100%);
padding: 3rem 1.5rem;
}
.expert-container { max-width: 800px; margin: 0 auto; }
.page-header { text-align: center; margin-bottom: 1.5rem; }
.page-header h1 {
font-size: 2rem; font-weight: 700; color: var(--text-color);
display: flex; align-items: center; justify-content: center; gap: 0.75rem;
}
.page-header h1 i {
background: linear-gradient(135deg, #10b981, #06b6d4);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.page-header .subtitle { color: var(--text-light); font-size: 1rem; }
/* 差异化说明条 */
.diff-badge {
background: white; border: 1px dashed #10b981; border-radius: 10px;
padding: 0.75rem 1rem; margin-bottom: 1.5rem; display: flex;
align-items: center; gap: 0.75rem; font-size: 0.85rem;
}
.badge-tag {
background: linear-gradient(135deg, #10b981, #06b6d4); color: white;
padding: 0.2rem 0.6rem; border-radius: 4px; font-weight: 600;
white-space: nowrap;
}
.diff-detail { color: var(--text-secondary); }
.flow-steps {
display: flex; align-items: center; justify-content: center;
gap: 0.75rem; margin-bottom: 2rem; padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.step { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; }
.step-num {
width: 36px; height: 36px; border-radius: 50%;
background: linear-gradient(135deg, #10b981, #06b6d4);
color: white; display: flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 0.9rem;
}
.step-label { font-size: 0.8rem; color: var(--text-light); font-weight: 500; }
.step-arrow { color: var(--text-muted); font-size: 0.85rem; margin-top: -14px; }
/* 表单 */
.expert-card {
background: white; border-radius: 16px; padding: 2rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
}
.input-group { margin-bottom: 1.5rem; }
.input-group label { display: block; font-weight: 600; font-size: 0.95rem; margin-bottom: 0.5rem; }
.input-group textarea {
width: 100%; padding: 1rem; border: 2px solid var(--border-color);
border-radius: 12px; font-size: 0.95rem; font-family: inherit; line-height: 1.6;
resize: vertical; min-height: 140px; transition: border-color 0.2s ease;
}
.input-group textarea:focus { outline: none; border-color: #10b981; box-shadow: 0 0 0 3px rgba(16,185,129,0.1); }
.input-hint { display: block; margin-top: 0.5rem; font-size: 0.8rem; color: var(--text-muted); }
button[type="submit"] {
width: 100%; padding: 0.875rem;
background: linear-gradient(135deg, #10b981, #06b6d4);
color: white; border: none; border-radius: 12px; font-size: 1.05rem;
font-weight: 600; cursor: pointer; transition: all 0.2s ease;
display: flex; align-items: center; justify-content: center; gap: 0.5rem;
}
button[type="submit"]:hover { transform: translateY(-1px); box-shadow: 0 4px 16px rgba(16,185,129,0.3); }
button[type="submit"]:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
/* 加载 */
.loading-card {
background: white; border-radius: 16px; padding: 3rem 2rem; text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.06); margin-top: 1.5rem;
}
.loading-spinner {
width: 48px; height: 48px; border: 3px solid var(--border-color);
border-top-color: #10b981; border-radius: 50%; margin: 0 auto 1.5rem;
animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: 1.05rem; color: var(--text-color); font-weight: 500; margin-bottom: 0.5rem; }
.loading-sub { font-size: 0.85rem; color: var(--text-muted); }
/* 结果 */
.result-card {
background: white; border-radius: 16px; overflow: hidden; margin-top: 1.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
animation: fadeUp 0.4s ease;
}
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.result-section { padding: 1.5rem 2rem; }
.result-section + .result-section { border-top: 1px solid var(--border-color); }
.result-section h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
.result-section h3 i { color: #10b981; }
.analysis-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
.analysis-item { background: #f0fdf4; border-radius: 10px; padding: 1rem; }
.analysis-label { display: block; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.35rem; }
.analysis-value { font-weight: 600; color: var(--text-color); font-size: 0.95rem; }
.analysis-list { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.analysis-list-item h4 { font-size: 0.85rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 0.5rem; }
.analysis-list-item ul { margin: 0; padding-left: 1.25rem; }
.analysis-list-item li { font-size: 0.875rem; color: var(--text-color); margin-bottom: 0.25rem; }
.result-header { display: flex; justify-content: space-between; align-items: center; }
.result-header-right { display: flex; align-items: center; gap: 0.75rem; }
.result-header h3 { margin-bottom: 0; }
.method-chip {
background: #d1fae5; color: #065f46; font-size: 0.75rem; padding: 0.25rem 0.6rem;
border-radius: 20px; font-weight: 600;
}
.btn-copy {
display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.875rem;
border-radius: 6px; border: 1px solid var(--border-color); background: white;
cursor: pointer; font-size: 0.85rem; color: var(--text-secondary); transition: all 0.2s ease;
}
.btn-copy:hover { background: #f1f5f9; color: #10b981; }
.btn-copy.copied { background: #10b981; color: white; border-color: #10b981; }
.result-content {
margin-top: 1rem; background: #f8fafc; border-radius: 10px; padding: 1.25rem;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; font-size: 0.9rem;
line-height: 1.7; white-space: pre-wrap; word-break: break-word; color: var(--text-color);
}
/* Toast */
.toast-container { position: fixed; top: 1rem; right: 1rem; z-index: 9999; display: flex; flex-direction: column; gap: 0.5rem; }
.toast { padding: 0.75rem 1.25rem; border-radius: 8px; color: white; font-size: 0.875rem; font-weight: 500; animation: slideIn 0.3s ease; }
.toast.error { background: #ef4444; }
.toast.success { background: #10b981; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@media (max-width: 768px) {
.expert-layout { padding: 1.5rem 1rem; }
.expert-card { padding: 1.25rem; }
.analysis-grid { grid-template-columns: 1fr; }
.analysis-list { grid-template-columns: 1fr; }
}
</style>
{% endblock %}
{% block scripts %}
<script>
(function() {
const form = document.getElementById('expertForm');
const textarea = document.getElementById('inputText');
const generateBtn = document.getElementById('generateBtn');
const loadingArea = document.getElementById('loadingArea');
const resultArea = document.getElementById('resultArea');
function showToast(message, type) {
const container = document.getElementById('toastContainer');
const toast = document.createElement('div');
toast.className = 'toast ' + type;
toast.textContent = message;
container.appendChild(toast);
setTimeout(function() { toast.style.opacity = '0'; toast.style.transition = 'all 0.3s ease'; setTimeout(function() { toast.remove(); }, 300); }, 3500);
}
form.addEventListener('submit', async function(e) {
e.preventDefault();
var input = textarea.value.trim();
if (!input) { showToast('请输入您的需求描述', 'error'); textarea.focus(); return; }
if (input.length < 10) { showToast('需求描述过于简短请至少输入10个字', 'error'); textarea.focus(); return; }
loadingArea.style.display = '';
resultArea.style.display = 'none';
generateBtn.disabled = true;
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 生成中...';
try {
var startTime = Date.now();
var response = await fetch('/api/expert-generate-2/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input_text: input })
});
var elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
var data = await response.json();
if (data.code === 200) {
document.getElementById('coreIntent').textContent = data.data.intent_analysis.core_intent;
document.getElementById('domain').textContent = data.data.intent_analysis.domain;
document.getElementById('expectedOutput').textContent = data.data.intent_analysis.expected_output;
document.getElementById('keyRequirements').innerHTML = data.data.intent_analysis.key_requirements.map(function(r) { return '<li>' + r + '</li>'; }).join('');
document.getElementById('constraints').innerHTML = data.data.intent_analysis.constraints.map(function(c) { return '<li>' + c + '</li>'; }).join('');
document.getElementById('generatedPrompt').textContent = data.data.generated_prompt;
// 显示耗时
var chip = document.querySelector('.method-chip');
chip.textContent = '仅 1 次 API · ' + elapsed + 's';
loadingArea.style.display = 'none';
resultArea.style.display = '';
resultArea.scrollIntoView({ behavior: 'smooth', block: 'start' });
} else {
loadingArea.style.display = 'none';
showToast(data.message || '生成失败,请稍后重试', 'error');
}
} catch (err) {
console.error('Error:', err);
loadingArea.style.display = 'none';
showToast('网络请求失败,请检查网络连接', 'error');
} finally {
generateBtn.disabled = false;
generateBtn.innerHTML = '<i class="fas fa-magic"></i> 快速生成';
}
});
document.getElementById('copyBtn').addEventListener('click', function() {
var text = document.getElementById('generatedPrompt').textContent;
if (!text) return;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(showCopyFeedback).catch(function() { showToast('复制失败', 'error'); });
} else {
var ta = document.createElement('textarea');
ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px';
document.body.appendChild(ta); ta.select(); document.execCommand('copy');
document.body.removeChild(ta); showCopyFeedback();
}
});
function showCopyFeedback() {
var btn = document.getElementById('copyBtn');
btn.innerHTML = '<i class="fas fa-check"></i> 已复制'; btn.classList.add('copied');
setTimeout(function() { btn.innerHTML = '<i class="fas fa-copy"></i> 复制'; btn.classList.remove('copied'); }, 2000);
}
})();
</script>
{% endblock %}

View File

@@ -1,195 +1,431 @@
{% extends "base.html" %}
{% block title %}智能提示词优化3号专家 - 提示词大师{% endblock %}
{% block title %}3号专家领域专精{% endblock %}
{% block content %}
<div class="container mt-4">
<div class="row">
<div class="col-md-8 offset-md-2">
<div class="d-flex justify-content-between align-items-center mb-4 flex-wrap gap-2">
<h2>智能提示词优化3号专家</h2>
<div class="d-flex gap-2">
<a href="{{ url_for('history.history_page') }}" class="btn btn-outline-secondary"><i class="fas fa-history me-1"></i>查看历史</a>
<a href="{{ url_for('main.index') }}" class="btn btn-outline-primary">
<i class="fas fa-arrow-left"></i> 返回基础模式
</a>
<div class="expert-layout">
<div class="expert-container">
<div class="page-header">
<h1><i class="fas fa-microscope"></i> 提示词生成器 3号</h1>
<p class="subtitle">方向B · 两次AI调用 + 领域专属结构 · 更专业精准</p>
</div>
<div class="diff-badge">
<span class="badge-tag">vs 原始专家模式</span>
<span class="diff-detail">每个领域使用完全不同的输出结构——技术规格书/创意简报/分析框架书/决策建议书</span>
</div>
<div class="expert-card">
<div class="flow-steps">
<div class="step">
<span class="step-num">1</span>
<span class="step-label">意图分析</span>
</div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step">
<span class="step-num">2</span>
<span class="step-label">领域匹配</span>
</div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step">
<span class="step-num">3</span>
<span class="step-label">专属生成</span>
</div>
</div>
<div class="card">
<div class="card-body">
<form id="expertForm3">
<div class="mb-3">
<label for="input_text" class="form-label">请描述您的需求</label>
<textarea class="form-control" id="input_text" name="input_text" rows="4" required></textarea>
<div class="form-text">详细描述您的需求,系统将进行专业分析并生成高质量提示词</div>
<form id="expertForm">
<div class="input-group">
<label for="inputText">请描述您的需求</label>
<textarea id="inputText" rows="5"
placeholder="例如:我需要写一份关于新产品发布的营销文案,目标受众是年轻白领..."></textarea>
<span class="input-hint">系统将匹配领域专属模板,生成结构化专业提示词</span>
</div>
<div class="advanced-toggle" id="advancedToggle">
<i class="fas fa-sliders-h"></i> 高级参数
</div>
<div class="advanced-panel" id="advancedPanel" style="display:none;">
<div class="param-row">
<label>Temperature <span class="param-hint">(0-2)</span></label>
<input type="number" id="paramTemperature" value="0.7" min="0" max="2" step="0.1">
</div>
<div class="param-row">
<label>Max Tokens <span class="param-hint">(100-4000)</span></label>
<input type="number" id="paramMaxTokens" value="1000" min="100" max="4000" step="100">
</div>
<div class="param-row">
<label>Timeout <span class="param-hint">秒 (10-300)</span></label>
<input type="number" id="paramTimeout" value="60" min="10" max="300" step="10">
</div>
</div>
<button type="submit" id="generateBtn">
<i class="fas fa-magic"></i> 开始生成
</button>
</form>
</div>
<div id="loadingArea" style="display:none;">
<div class="loading-card">
<div class="loading-spinner"></div>
<p class="loading-text" id="loadingText">正在分析您的需求意图...</p>
<div class="loading-steps">
<span class="load-step active">识别意图</span>
<span class="load-step">匹配模板</span>
<span class="load-step">专属生成</span>
</div>
</div>
</div>
<div id="resultArea" style="display:none;">
<div class="result-card">
<div class="result-section">
<h3><i class="fas fa-search"></i> 意图分析</h3>
<div class="analysis-grid">
<div class="analysis-item">
<span class="analysis-label">核心意图</span>
<span class="analysis-value" id="coreIntent"></span>
</div>
<div class="mb-3">
<button class="btn btn-link btn-sm p-0 text-secondary" type="button" data-bs-toggle="collapse" data-bs-target="#advancedSettings" aria-expanded="false">
<i class="fas fa-sliders-h me-1"></i>高级参数
</button>
<div class="collapse mt-2" id="advancedSettings">
<div class="card card-body bg-light">
<div class="row g-2">
<div class="col-md-4">
<label for="paramTemperature" class="form-label small">temperature</label>
<input type="number" class="form-control form-control-sm" id="paramTemperature" value="0.7" min="0" max="2" step="0.1" title="生成随机性0更确定2更随机">
<div class="form-text small">02默认0.7</div>
</div>
<div class="col-md-4">
<label for="paramMaxTokens" class="form-label small">max_tokens</label>
<input type="number" class="form-control form-control-sm" id="paramMaxTokens" value="1000" min="100" max="4000" step="100" title="最大生成 token 数">
<div class="form-text small">1004000默认1000</div>
</div>
<div class="col-md-4">
<label for="paramTimeout" class="form-label small">timeout</label>
<input type="number" class="form-control form-control-sm" id="paramTimeout" value="60" min="10" max="300" step="10" title="请求超时时间">
<div class="form-text small">10300秒默认60</div>
</div>
</div>
</div>
</div>
<div class="analysis-item">
<span class="analysis-label">专业领域</span>
<span class="analysis-value" id="domain"></span>
</div>
<button type="submit" class="btn btn-primary" id="generateBtn">
<i class="fas fa-magic"></i> 生成专家提示词
</button>
</form>
</div>
</div>
<div id="loadingIndicator" class="text-center mt-4 d-none">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">生成中...</span>
</div>
<p class="mt-2">正在分析需求并生成专业提示词...</p>
</div>
<div id="resultCard" class="card mt-4 d-none">
<div class="card-body">
<h5 class="card-title mb-4">生成结果</h5>
<div class="mb-4">
<h6 class="mb-3">需求分析</h6>
<div id="intentAnalysis" class="border rounded p-3 bg-light">
<div class="row">
<div class="col-md-6">
<p><strong>核心意图:</strong><span id="coreIntent"></span></p>
<p><strong>专业领域:</strong><span id="domain"></span></p>
</div>
<div class="col-md-6">
<p><strong>预期输出:</strong><span id="expectedOutput"></span></p>
</div>
</div>
<div class="mt-2">
<p><strong>关键需求:</strong></p>
<ul id="keyRequirements" class="mb-0"></ul>
</div>
<div class="mt-2">
<p><strong>约束条件:</strong></p>
<ul id="constraints" class="mb-0"></ul>
</div>
<div class="analysis-item">
<span class="analysis-label">预期输出</span>
<span class="analysis-value" id="expectedOutput"></span>
</div>
</div>
<div>
<h6 class="mb-3">生成的专家提示词</h6>
<div id="generatedPrompt" class="border rounded p-3"></div>
<div class="mt-3">
<button class="btn btn-sm btn-primary copy-btn" data-target="generatedPrompt">
<i class="fas fa-copy"></i> 复制提示词
<div class="analysis-list">
<div class="analysis-list-item">
<h4>关键需求</h4>
<ul id="keyRequirements"></ul>
</div>
<div class="analysis-list-item">
<h4>约束条件</h4>
<ul id="constraints"></ul>
</div>
</div>
</div>
<div class="result-section">
<div class="result-header">
<h3><i class="fas fa-star"></i> 领域专属生成结果</h3>
<div class="result-header-right">
<span class="domain-chip" id="domainChip"></span>
<button class="btn-copy" id="copyBtn">
<i class="fas fa-copy"></i> 复制
</button>
</div>
</div>
<div class="result-content" id="generatedPrompt"></div>
</div>
</div>
</div>
</div>
</div>
{% block scripts %}
<script>
document.getElementById('expertForm3').addEventListener('submit', async function(e) {
e.preventDefault();
var form = e.target;
if (form.dataset.submitting === '1') return;
form.dataset.submitting = '1';
var generateBtn = document.getElementById('generateBtn');
generateBtn.disabled = true;
var input_text = document.getElementById('input_text').value.trim();
var loadingIndicator = document.getElementById('loadingIndicator');
var resultCard = document.getElementById('resultCard');
var temperature = parseFloat(document.getElementById('paramTemperature').value) || 0.7;
var maxTokens = parseInt(document.getElementById('paramMaxTokens').value, 10) || 1000;
var timeout = parseInt(document.getElementById('paramTimeout').value, 10) || 60;
if (!input_text) { form.dataset.submitting = ''; generateBtn.disabled = false; alert('请输入您的需求'); return; }
loadingIndicator.classList.remove('d-none');
resultCard.classList.add('d-none');
try {
var response = await fetch('/api/expert-generate-3/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
input_text: input_text,
temperature: temperature,
max_tokens: maxTokens,
timeout: timeout
})
});
var data = await response.json();
if (data.code === 200) {
document.getElementById('coreIntent').textContent = data.data.intent_analysis.core_intent || '';
document.getElementById('domain').textContent = data.data.intent_analysis.domain || '';
document.getElementById('expectedOutput').textContent = data.data.intent_analysis.expected_output || '';
document.getElementById('keyRequirements').innerHTML = (data.data.intent_analysis.key_requirements || [])
.map(function(r){ return '<li>'+r+'</li>'; }).join('');
document.getElementById('constraints').innerHTML = (data.data.intent_analysis.constraints || [])
.map(function(c){ return '<li>'+c+'</li>'; }).join('');
document.getElementById('generatedPrompt').textContent = data.data.generated_prompt || '';
resultCard.classList.remove('d-none');
} else {
alert('生成失败:' + (data.message || '请重试'));
}
} catch (err) {
console.error(err);
alert('请求失败,请稍后重试');
} finally {
loadingIndicator.classList.add('d-none');
generateBtn.disabled = false;
form.dataset.submitting = '';
}
});
function copyToClipboard(text) {
var ta = document.createElement('textarea');
ta.value = text;
document.body.appendChild(ta);
ta.select();
ta.setSelectionRange(0, 99999);
try {
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(text).then(showCopySuccess).catch(function(){ document.execCommand('copy'); showCopySuccess(); });
} else { document.execCommand('copy'); showCopySuccess(); }
} catch(e){}
document.body.removeChild(ta);
}
function showCopySuccess() {
var btn = document.querySelector('.copy-btn');
if (!btn) return;
var orig = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-check"></i> 已复制';
btn.classList.add('copied');
setTimeout(function(){ btn.innerHTML = orig; btn.classList.remove('copied'); }, 2000);
}
document.addEventListener('DOMContentLoaded', function() {
var copyBtn = document.querySelector('.copy-btn');
if (copyBtn) copyBtn.addEventListener('click', function() {
var text = document.getElementById(this.dataset.target).textContent;
copyToClipboard(text);
});
});
</script>
<div class="toast-container" id="toastContainer"></div>
{% endblock %}
{% block extra_css %}
<style>
.copy-btn { padding: 8px 16px; background-color: #4a90e2; color: white; border: none; border-radius: 4px; cursor: pointer; }
.copy-btn:hover { background-color: #357abd; }
.copy-btn.copied { background-color: #45a049; }
#generatedPrompt { background-color: #f8f9fa; padding: 1rem; white-space: pre-wrap; font-family: monospace; line-height: 1.5; }
#loadingIndicator { margin: 2rem 0; }
.spinner-border { width: 3rem; height: 3rem; }
.expert-layout {
min-height: calc(100vh - 120px);
background: linear-gradient(135deg, #faf5ff 0%, #eff6ff 100%);
padding: 3rem 1.5rem;
}
.expert-container { max-width: 800px; margin: 0 auto; }
.page-header { text-align: center; margin-bottom: 1.5rem; }
.page-header h1 {
font-size: 2rem; font-weight: 700; color: var(--text-color);
display: flex; align-items: center; justify-content: center; gap: 0.75rem;
}
.page-header h1 i {
background: linear-gradient(135deg, #8b5cf6, #6366f1);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.page-header .subtitle { color: var(--text-light); font-size: 1rem; }
/* 差异化说明条 */
.diff-badge {
background: white; border: 1px dashed #8b5cf6; border-radius: 10px;
padding: 0.75rem 1rem; margin-bottom: 1.5rem; display: flex;
align-items: center; gap: 0.75rem; font-size: 0.85rem;
}
.badge-tag {
background: linear-gradient(135deg, #8b5cf6, #6366f1); color: white;
padding: 0.2rem 0.6rem; border-radius: 4px; font-weight: 600;
white-space: nowrap;
}
.diff-detail { color: var(--text-secondary); }
.flow-steps {
display: flex; align-items: center; justify-content: center;
gap: 0.75rem; margin-bottom: 2rem; padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.step { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; }
.step-num {
width: 36px; height: 36px; border-radius: 50%;
background: linear-gradient(135deg, #8b5cf6, #6366f1);
color: white; display: flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 0.9rem;
}
.step-label { font-size: 0.8rem; color: var(--text-light); font-weight: 500; }
.step-arrow { color: var(--text-muted); font-size: 0.85rem; margin-top: -14px; }
/* 表单 */
.expert-card {
background: white; border-radius: 16px; padding: 2rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
}
.input-group { margin-bottom: 1.5rem; }
.input-group label { display: block; font-weight: 600; font-size: 0.95rem; margin-bottom: 0.5rem; }
.input-group textarea {
width: 100%; padding: 1rem; border: 2px solid var(--border-color);
border-radius: 12px; font-size: 0.95rem; font-family: inherit; line-height: 1.6;
resize: vertical; min-height: 140px; transition: border-color 0.2s ease;
}
.input-group textarea:focus { outline: none; border-color: #8b5cf6; box-shadow: 0 0 0 3px rgba(139,92,246,0.1); }
.input-hint { display: block; margin-top: 0.5rem; font-size: 0.8rem; color: var(--text-muted); }
/* 高级参数 */
.advanced-toggle {
display: inline-flex; align-items: center; gap: 0.4rem;
color: var(--text-muted); font-size: 0.85rem; cursor: pointer;
padding: 0.3rem 0; margin-bottom: 1rem; user-select: none;
}
.advanced-toggle:hover { color: var(--primary-color); }
.advanced-panel {
background: #f8fafc; border-radius: 10px; padding: 1rem; margin-bottom: 1rem;
display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75rem;
}
.param-row label { display: block; font-size: 0.8rem; font-weight: 500; margin-bottom: 0.25rem; }
.param-hint { color: var(--text-muted); font-weight: 400; }
.param-row input {
width: 100%; padding: 0.4rem 0.5rem; border: 1px solid var(--border-color);
border-radius: 6px; font-size: 0.9rem;
}
.param-row input:focus { outline: none; border-color: #8b5cf6; }
button[type="submit"] {
width: 100%; padding: 0.875rem;
background: linear-gradient(135deg, #8b5cf6, #6366f1);
color: white; border: none; border-radius: 12px; font-size: 1.05rem;
font-weight: 600; cursor: pointer; transition: all 0.2s ease;
display: flex; align-items: center; justify-content: center; gap: 0.5rem;
}
button[type="submit"]:hover { transform: translateY(-1px); box-shadow: 0 4px 16px rgba(139,92,246,0.3); }
button[type="submit"]:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
/* 加载 */
.loading-card {
background: white; border-radius: 16px; padding: 3rem 2rem; text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.06); margin-top: 1.5rem;
}
.loading-spinner {
width: 48px; height: 48px; border: 3px solid var(--border-color);
border-top-color: #8b5cf6; border-radius: 50%; margin: 0 auto 1.5rem;
animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: 1.05rem; color: var(--text-color); font-weight: 500; margin-bottom: 1.5rem; }
.loading-steps { display: flex; gap: 1.5rem; justify-content: center; }
.load-step { font-size: 0.8rem; color: var(--text-muted); padding: 0.35rem 0.75rem; border-radius: 20px; background: #f1f5f9; transition: all 0.5s ease; }
.load-step.active { background: linear-gradient(135deg, #8b5cf6, #6366f1); color: white; }
/* 结果 */
.result-card {
background: white; border-radius: 16px; overflow: hidden; margin-top: 1.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
animation: fadeUp 0.4s ease;
}
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.result-section { padding: 1.5rem 2rem; }
.result-section + .result-section { border-top: 1px solid var(--border-color); }
.result-section h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
.result-section h3 i { color: var(--primary-color); }
.analysis-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
.analysis-item { background: #faf5ff; border-radius: 10px; padding: 1rem; }
.analysis-label { display: block; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.35rem; }
.analysis-value { font-weight: 600; color: var(--text-color); font-size: 0.95rem; }
.analysis-list { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.analysis-list-item h4 { font-size: 0.85rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 0.5rem; }
.analysis-list-item ul { margin: 0; padding-left: 1.25rem; }
.analysis-list-item li { font-size: 0.875rem; color: var(--text-color); margin-bottom: 0.25rem; }
.result-header { display: flex; justify-content: space-between; align-items: center; }
.result-header-right { display: flex; align-items: center; gap: 0.75rem; }
.result-header h3 { margin-bottom: 0; }
.domain-chip {
background: #ede9fe; color: #5b21b6; font-size: 0.75rem; padding: 0.25rem 0.6rem;
border-radius: 20px; font-weight: 600;
}
.btn-copy {
display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.875rem;
border-radius: 6px; border: 1px solid var(--border-color); background: white;
cursor: pointer; font-size: 0.85rem; color: var(--text-secondary); transition: all 0.2s ease;
}
.btn-copy:hover { background: #f1f5f9; color: var(--primary-color); }
.btn-copy.copied { background: #10b981; color: white; border-color: #10b981; }
.result-content {
margin-top: 1rem; background: #f8fafc; border-radius: 10px; padding: 1.25rem;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; font-size: 0.9rem;
line-height: 1.7; white-space: pre-wrap; word-break: break-word; color: var(--text-color);
}
/* Toast */
.toast-container { position: fixed; top: 1rem; right: 1rem; z-index: 9999; display: flex; flex-direction: column; gap: 0.5rem; }
.toast { padding: 0.75rem 1.25rem; border-radius: 8px; color: white; font-size: 0.875rem; font-weight: 500; animation: slideIn 0.3s ease; }
.toast.error { background: #ef4444; }
.toast.success { background: #10b981; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@media (max-width: 768px) {
.expert-layout { padding: 1.5rem 1rem; }
.expert-card { padding: 1.25rem; }
.analysis-grid { grid-template-columns: 1fr; }
.analysis-list { grid-template-columns: 1fr; }
.advanced-panel { grid-template-columns: 1fr; }
}
</style>
{% endblock %}
{% block scripts %}
<script>
(function() {
const form = document.getElementById('expertForm');
const textarea = document.getElementById('inputText');
const generateBtn = document.getElementById('generateBtn');
const loadingArea = document.getElementById('loadingArea');
const loadingText = document.getElementById('loadingText');
const resultArea = document.getElementById('resultArea');
const loadSteps = document.querySelectorAll('.load-step');
// 领域标签映射
var domainLabels = {
'技术': '技术规格书',
'创意': '创意简报',
'分析': '分析框架书',
'咨询': '决策建议书'
};
var domainIcons = {
'技术': 'fa-cogs',
'创意': 'fa-palette',
'分析': 'fa-chart-line',
'咨询': 'fa-briefcase'
};
// 高级参数折叠
document.getElementById('advancedToggle').addEventListener('click', function() {
var panel = document.getElementById('advancedPanel');
var show = panel.style.display === 'none';
panel.style.display = show ? '' : 'none';
this.querySelector('i').className = show ? 'fas fa-chevron-up' : 'fas fa-sliders-h';
});
function showToast(message, type) {
var container = document.getElementById('toastContainer');
var toast = document.createElement('div');
toast.className = 'toast ' + type;
toast.textContent = message;
container.appendChild(toast);
setTimeout(function() { toast.style.opacity = '0'; toast.style.transition = 'all 0.3s ease'; setTimeout(function() { toast.remove(); }, 300); }, 3500);
}
function animateLoadStep(index) {
loadSteps.forEach(function(s, i) { s.classList.toggle('active', i <= index); });
}
form.addEventListener('submit', async function(e) {
e.preventDefault();
var input = textarea.value.trim();
if (!input) { showToast('请输入您的需求描述', 'error'); textarea.focus(); return; }
if (input.length < 10) { showToast('需求描述过于简短请至少输入10个字', 'error'); textarea.focus(); return; }
var temperature = parseFloat(document.getElementById('paramTemperature').value) || 0.7;
var maxTokens = parseInt(document.getElementById('paramMaxTokens').value) || 1000;
var timeout = parseInt(document.getElementById('paramTimeout').value) || 60;
loadingArea.style.display = '';
resultArea.style.display = 'none';
generateBtn.disabled = true;
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 生成中...';
animateLoadStep(0); loadingText.textContent = '正在分析您的需求意图...';
var t1 = setTimeout(function() { animateLoadStep(1); loadingText.textContent = '匹配领域专属模板...'; }, 1200);
var t2 = setTimeout(function() { animateLoadStep(2); loadingText.textContent = '正在生成领域专属提示词...'; }, 3000);
try {
var response = await fetch('/api/expert-generate-3/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
input_text: input,
temperature: temperature,
max_tokens: maxTokens,
timeout: timeout
})
});
var data = await response.json();
clearTimeout(t1); clearTimeout(t2);
if (data.code === 200) {
document.getElementById('coreIntent').textContent = data.data.intent_analysis.core_intent;
document.getElementById('domain').textContent = data.data.intent_analysis.domain;
document.getElementById('expectedOutput').textContent = data.data.intent_analysis.expected_output;
document.getElementById('keyRequirements').innerHTML = data.data.intent_analysis.key_requirements.map(function(r) { return '<li>' + r + '</li>'; }).join('');
document.getElementById('constraints').innerHTML = data.data.intent_analysis.constraints.map(function(c) { return '<li>' + c + '</li>'; }).join('');
document.getElementById('generatedPrompt').textContent = data.data.generated_prompt;
// 显示使用的领域模板
var intent = data.data.intent_analysis.core_intent;
var chip = document.getElementById('domainChip');
chip.innerHTML = '<i class="fas ' + (domainIcons[intent] || 'fa-file-alt') + '"></i> ' + (domainLabels[intent] || intent);
loadingArea.style.display = 'none';
resultArea.style.display = '';
resultArea.scrollIntoView({ behavior: 'smooth', block: 'start' });
} else {
loadingArea.style.display = 'none';
showToast(data.message || '生成失败,请稍后重试', 'error');
}
} catch (err) {
console.error('Error:', err);
clearTimeout(t1); clearTimeout(t2);
loadingArea.style.display = 'none';
showToast('网络请求失败,请检查网络连接', 'error');
} finally {
generateBtn.disabled = false;
generateBtn.innerHTML = '<i class="fas fa-magic"></i> 开始生成';
loadSteps.forEach(function(s) { s.classList.remove('active'); });
}
});
document.getElementById('copyBtn').addEventListener('click', function() {
var text = document.getElementById('generatedPrompt').textContent;
if (!text) return;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(showCopyFeedback).catch(function() { showToast('复制失败', 'error'); });
} else {
var ta = document.createElement('textarea');
ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px';
document.body.appendChild(ta); ta.select(); document.execCommand('copy');
document.body.removeChild(ta); showCopyFeedback();
}
});
function showCopyFeedback() {
var btn = document.getElementById('copyBtn');
btn.innerHTML = '<i class="fas fa-check"></i> 已复制'; btn.classList.add('copied');
setTimeout(function() { btn.innerHTML = '<i class="fas fa-copy"></i> 复制'; btn.classList.remove('copied'); }, 2000);
}
})();
</script>
{% endblock %}

View File

@@ -0,0 +1,414 @@
{% extends "base.html" %}
{% block title %}4号专家消歧增强{% endblock %}
{% block content %}
<div class="expert-layout">
<div class="expert-container">
<div class="page-header">
<h1><i class="fas fa-crosshairs"></i> 提示词生成器 4号</h1>
<p class="subtitle">方向C · 消歧增强意图分析 + 领域专属结构 · 精准度优先</p>
</div>
<div class="diff-badge">
<span class="badge-tag">核心改进</span>
<span class="diff-detail">阶段1 引入中文多义词消歧规则表(设计/方案/分析),输出置信度 + 备选意图 + 判定依据</span>
</div>
<div class="expert-card">
<div class="flow-steps">
<div class="step">
<span class="step-num">1</span>
<span class="step-label">消歧分析</span>
</div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step">
<span class="step-num">2</span>
<span class="step-label">置信评估</span>
</div>
<i class="fas fa-arrow-right step-arrow"></i>
<div class="step">
<span class="step-num">3</span>
<span class="step-label">专属生成</span>
</div>
</div>
<form id="expertForm">
<div class="input-group">
<label for="inputText">请描述您的需求</label>
<textarea id="inputText" rows="5"
placeholder="例如写一篇关于agent协助的设计方案..."></textarea>
<span class="input-hint">系统内置中文消歧规则,能区分"系统设计"vs"创意设计"</span>
</div>
<button type="submit" id="generateBtn">
<i class="fas fa-magic"></i> 精准生成
</button>
</form>
</div>
<div id="loadingArea" style="display:none;">
<div class="loading-card">
<div class="loading-spinner"></div>
<p class="loading-text" id="loadingText">正在消歧分析...</p>
<div class="loading-steps">
<span class="load-step active">消歧识别</span>
<span class="load-step">置信评估</span>
<span class="load-step">专属生成</span>
</div>
</div>
</div>
<div id="resultArea" style="display:none;">
<div class="result-card">
<!-- 消歧信息 -->
<div class="result-section disamb-section">
<h3><i class="fas fa-balance-scale"></i> 消歧分析</h3>
<div class="disamb-grid">
<div class="disamb-item">
<span class="disamb-label">主判定</span>
<span class="disamb-value" id="coreIntent"></span>
</div>
<div class="disamb-item">
<span class="disamb-label">子类</span>
<span class="disamb-value" id="subCategory"></span>
</div>
<div class="disamb-item">
<span class="disamb-label">置信度</span>
<span class="disamb-value" id="confidenceVal">
<span id="confidenceBar" class="confidence-bar"></span>
</span>
</div>
<div class="disamb-item full-width">
<span class="disamb-label">判定依据</span>
<span class="disamb-value" id="disambNote" style="font-size:0.85rem;"></span>
</div>
<div class="disamb-item" id="altIntentBox" style="display:none;">
<span class="disamb-label">备选理解</span>
<span class="disamb-value" id="altIntent"></span>
</div>
</div>
</div>
<!-- 意图分析 -->
<div class="result-section">
<h3><i class="fas fa-search"></i> 完整意图分析</h3>
<div class="analysis-grid">
<div class="analysis-item">
<span class="analysis-label">核心意图</span>
<span class="analysis-value" id="intentDisplay"></span>
</div>
<div class="analysis-item">
<span class="analysis-label">专业领域</span>
<span class="analysis-value" id="domain"></span>
</div>
<div class="analysis-item">
<span class="analysis-label">预期输出</span>
<span class="analysis-value" id="expectedOutput"></span>
</div>
</div>
<div class="analysis-list">
<div class="analysis-list-item">
<h4>关键需求</h4>
<ul id="keyRequirements"></ul>
</div>
<div class="analysis-list-item">
<h4>约束条件</h4>
<ul id="constraints"></ul>
</div>
</div>
</div>
<div class="result-section">
<div class="result-header">
<h3><i class="fas fa-star"></i> 生成的专家提示词</h3>
<button class="btn-copy" id="copyBtn"><i class="fas fa-copy"></i> 复制</button>
</div>
<div class="result-content" id="generatedPrompt"></div>
</div>
</div>
</div>
</div>
</div>
<div class="toast-container" id="toastContainer"></div>
{% endblock %}
{% block extra_css %}
<style>
.expert-layout {
min-height: calc(100vh - 120px);
background: linear-gradient(135deg, #fefce8 0%, #faf5ff 100%);
padding: 3rem 1.5rem;
}
.expert-container { max-width: 800px; margin: 0 auto; }
.page-header { text-align: center; margin-bottom: 1.5rem; }
.page-header h1 {
font-size: 2rem; font-weight: 700; color: var(--text-color);
display: flex; align-items: center; justify-content: center; gap: 0.75rem;
}
.page-header h1 i {
background: linear-gradient(135deg, #f59e0b, #ef4444);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
}
.page-header .subtitle { color: var(--text-light); font-size: 1rem; }
.diff-badge {
background: white; border: 1px dashed #f59e0b; border-radius: 10px;
padding: 0.75rem 1rem; margin-bottom: 1.5rem; display: flex;
align-items: center; gap: 0.75rem; font-size: 0.85rem;
}
.badge-tag {
background: linear-gradient(135deg, #f59e0b, #ef4444); color: white;
padding: 0.2rem 0.6rem; border-radius: 4px; font-weight: 600; white-space: nowrap;
}
.diff-detail { color: var(--text-secondary); }
.flow-steps {
display: flex; align-items: center; justify-content: center;
gap: 0.75rem; margin-bottom: 2rem; padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color);
}
.step { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; }
.step-num {
width: 36px; height: 36px; border-radius: 50%;
background: linear-gradient(135deg, #f59e0b, #ef4444);
color: white; display: flex; align-items: center; justify-content: center;
font-weight: 700; font-size: 0.9rem;
}
.step-label { font-size: 0.8rem; color: var(--text-light); font-weight: 500; }
.step-arrow { color: var(--text-muted); font-size: 0.85rem; margin-top: -14px; }
/* 表单 */
.expert-card {
background: white; border-radius: 16px; padding: 2rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
}
.input-group { margin-bottom: 1.5rem; }
.input-group label { display: block; font-weight: 600; font-size: 0.95rem; margin-bottom: 0.5rem; }
.input-group textarea {
width: 100%; padding: 1rem; border: 2px solid var(--border-color);
border-radius: 12px; font-size: 0.95rem; font-family: inherit; line-height: 1.6;
resize: vertical; min-height: 140px; transition: border-color 0.2s ease;
}
.input-group textarea:focus { outline: none; border-color: #f59e0b; box-shadow: 0 0 0 3px rgba(245,158,11,0.1); }
.input-hint { display: block; margin-top: 0.5rem; font-size: 0.8rem; color: var(--text-muted); }
button[type="submit"] {
width: 100%; padding: 0.875rem;
background: linear-gradient(135deg, #f59e0b, #ef4444);
color: white; border: none; border-radius: 12px; font-size: 1.05rem;
font-weight: 600; cursor: pointer; transition: all 0.2s ease;
display: flex; align-items: center; justify-content: center; gap: 0.5rem;
}
button[type="submit"]:hover { transform: translateY(-1px); box-shadow: 0 4px 16px rgba(245,158,11,0.3); }
button[type="submit"]:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }
/* 加载 */
.loading-card {
background: white; border-radius: 16px; padding: 3rem 2rem; text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.06); margin-top: 1.5rem;
}
.loading-spinner {
width: 48px; height: 48px; border: 3px solid var(--border-color);
border-top-color: #f59e0b; border-radius: 50%; margin: 0 auto 1.5rem;
animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
.loading-text { font-size: 1.05rem; color: var(--text-color); font-weight: 500; margin-bottom: 1.5rem; }
.loading-steps { display: flex; gap: 1.5rem; justify-content: center; }
.load-step { font-size: 0.8rem; color: var(--text-muted); padding: 0.35rem 0.75rem; border-radius: 20px; background: #f1f5f9; transition: all 0.5s ease; }
.load-step.active { background: linear-gradient(135deg, #f59e0b, #ef4444); color: white; }
/* 结果 */
.result-card {
background: white; border-radius: 16px; overflow: hidden; margin-top: 1.5rem;
box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
animation: fadeUp 0.4s ease;
}
@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.result-section { padding: 1.5rem 2rem; }
.result-section + .result-section { border-top: 1px solid var(--border-color); }
.result-section h3 { font-size: 1.1rem; font-weight: 600; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; }
/* 消歧专属区块 */
.disamb-section { background: #fffdf5; }
.disamb-section h3 i { color: #f59e0b; }
.disamb-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.disamb-item { background: white; border-radius: 10px; padding: 1rem; border: 1px solid #fef3c7; }
.disamb-item.full-width { grid-column: 1 / -1; }
.disamb-label { display: block; font-size: 0.72rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.35rem; }
.disamb-value { font-weight: 600; color: var(--text-color); font-size: 0.95rem; }
.confidence-bar {
display: inline-block; height: 8px; border-radius: 4px; transition: all 0.5s ease;
background: #10b981; min-width: 40px;
}
.analysis-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; margin-bottom: 1.5rem; }
.analysis-item { background: #fffbeb; border-radius: 10px; padding: 1rem; }
.analysis-label { display: block; font-size: 0.75rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.35rem; }
.analysis-value { font-weight: 600; color: var(--text-color); font-size: 0.95rem; }
.analysis-list { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
.analysis-list-item h4 { font-size: 0.85rem; font-weight: 600; color: var(--text-secondary); margin-bottom: 0.5rem; }
.analysis-list-item ul { margin: 0; padding-left: 1.25rem; }
.analysis-list-item li { font-size: 0.875rem; color: var(--text-color); margin-bottom: 0.25rem; }
.result-header { display: flex; justify-content: space-between; align-items: center; }
.result-header h3 { margin-bottom: 0; }
.btn-copy {
display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.4rem 0.875rem;
border-radius: 6px; border: 1px solid var(--border-color); background: white;
cursor: pointer; font-size: 0.85rem; color: var(--text-secondary); transition: all 0.2s ease;
}
.btn-copy:hover { background: #f1f5f9; color: #f59e0b; }
.btn-copy.copied { background: #10b981; color: white; border-color: #10b981; }
.result-content {
margin-top: 1rem; background: #f8fafc; border-radius: 10px; padding: 1.25rem;
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace; font-size: 0.9rem;
line-height: 1.7; white-space: pre-wrap; word-break: break-word; color: var(--text-color);
}
.toast-container { position: fixed; top: 1rem; right: 1rem; z-index: 9999; display: flex; flex-direction: column; gap: 0.5rem; }
.toast { padding: 0.75rem 1.25rem; border-radius: 8px; color: white; font-size: 0.875rem; font-weight: 500; animation: slideIn 0.3s ease; }
.toast.error { background: #ef4444; }
.toast.success { background: #10b981; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@media (max-width: 768px) {
.expert-layout { padding: 1.5rem 1rem; }
.expert-card { padding: 1.25rem; }
.disamb-grid { grid-template-columns: 1fr; }
.analysis-grid { grid-template-columns: 1fr; }
.analysis-list { grid-template-columns: 1fr; }
}
</style>
{% endblock %}
{% block scripts %}
<script>
(function() {
var form = document.getElementById('expertForm');
var textarea = document.getElementById('inputText');
var generateBtn = document.getElementById('generateBtn');
var loadingArea = document.getElementById('loadingArea');
var loadingText = document.getElementById('loadingText');
var resultArea = document.getElementById('resultArea');
var loadSteps = document.querySelectorAll('.load-step');
function showToast(msg, type) {
var c = document.getElementById('toastContainer');
var t = document.createElement('div');
t.className = 'toast ' + type;
t.textContent = msg;
c.appendChild(t);
setTimeout(function(){ t.style.opacity='0'; t.style.transition='all 0.3s ease'; setTimeout(function(){ t.remove(); }, 300); }, 3500);
}
function animateLoadStep(i) {
loadSteps.forEach(function(s, j) { s.classList.toggle('active', j <= i); });
}
form.addEventListener('submit', async function(e) {
e.preventDefault();
var input = textarea.value.trim();
if (!input) { showToast('请输入需求描述', 'error'); textarea.focus(); return; }
if (input.length < 10) { showToast('需求过短请至少输入10个字', 'error'); textarea.focus(); return; }
loadingArea.style.display = '';
resultArea.style.display = 'none';
generateBtn.disabled = true;
generateBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> 分析中...';
animateLoadStep(0); loadingText.textContent = '正在消歧分析(设计=系统设计 or 创意设计?)...';
var t1 = setTimeout(function(){ animateLoadStep(1); loadingText.textContent = '评估分类置信度...'; }, 1200);
var t2 = setTimeout(function(){ animateLoadStep(2); loadingText.textContent = '生成领域专属提示词...'; }, 3000);
try {
var resp = await fetch('/api/expert-generate-4/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ input_text: input })
});
var data = await resp.json();
clearTimeout(t1); clearTimeout(t2);
if (data.code === 200) {
var ia = data.data.intent_analysis;
var conf = ia.confidence || 0;
// 消歧区块
document.getElementById('coreIntent').textContent = ia.core_intent;
document.getElementById('subCategory').textContent = ia.sub_category || ia.domain;
document.getElementById('disambNote').textContent = ia.disambiguation_note || '未触发消歧规则';
var bar = document.getElementById('confidenceBar');
bar.style.width = (conf * 100) + '%';
bar.style.background = conf >= 0.85 ? '#10b981' : conf >= 0.7 ? '#f59e0b' : '#ef4444';
bar.textContent = (conf * 100).toFixed(0) + '%';
bar.style.display = 'inline-block';
bar.style.paddingLeft = '8px';
bar.style.paddingRight = '8px';
bar.style.color = 'white';
bar.style.fontSize = '0.75rem';
bar.style.lineHeight = '20px';
bar.style.minWidth = '50px';
bar.style.textAlign = 'center';
if (ia.alternative_intent && conf < 0.85) {
document.getElementById('altIntentBox').style.display = '';
document.getElementById('altIntent').textContent = ia.alternative_intent;
} else {
document.getElementById('altIntentBox').style.display = 'none';
}
// 分析区块
document.getElementById('intentDisplay').textContent = ia.core_intent;
document.getElementById('domain').textContent = ia.domain;
document.getElementById('expectedOutput').textContent = ia.expected_output;
document.getElementById('keyRequirements').innerHTML = (ia.key_requirements || []).map(function(r){ return '<li>'+r+'</li>'; }).join('');
document.getElementById('constraints').innerHTML = (ia.constraints || []).map(function(c){ return '<li>'+c+'</li>'; }).join('');
// 生成结果
document.getElementById('generatedPrompt').textContent = data.data.generated_prompt;
loadingArea.style.display = 'none';
resultArea.style.display = '';
resultArea.scrollIntoView({ behavior: 'smooth', block: 'start' });
} else {
loadingArea.style.display = 'none';
showToast(data.message || '生成失败', 'error');
}
} catch (err) {
console.error(err);
clearTimeout(t1); clearTimeout(t2);
loadingArea.style.display = 'none';
showToast('网络请求失败', 'error');
} finally {
generateBtn.disabled = false;
generateBtn.innerHTML = '<i class="fas fa-magic"></i> 精准生成';
loadSteps.forEach(function(s) { s.classList.remove('active'); });
}
});
document.getElementById('copyBtn').addEventListener('click', function() {
var text = document.getElementById('generatedPrompt').textContent;
if (!text) return;
if (navigator.clipboard) {
navigator.clipboard.writeText(text).then(function(){
var btn = document.getElementById('copyBtn');
btn.innerHTML = '<i class="fas fa-check"></i> 已复制'; btn.classList.add('copied');
setTimeout(function(){ btn.innerHTML = '<i class="fas fa-copy"></i> 复制'; btn.classList.remove('copied'); }, 2000);
}).catch(function(){ showToast('复制失败', 'error'); });
} else {
var ta = document.createElement('textarea');
ta.value = text; ta.style.position = 'fixed'; ta.style.left = '-9999px';
document.body.appendChild(ta); ta.select(); document.execCommand('copy');
document.body.removeChild(ta);
var btn = document.getElementById('copyBtn');
btn.innerHTML = '<i class="fas fa-check"></i> 已复制'; btn.classList.add('copied');
setTimeout(function(){ btn.innerHTML = '<i class="fas fa-copy"></i> 复制'; btn.classList.remove('copied'); }, 2000);
}
});
})();
</script>
{% endblock %}

View File

@@ -14,12 +14,6 @@
<a href="{{ url_for('expert_generate_5.expert_generate_5_page') }}" class="btn-expert btn-expert-5">
<i class="fas fa-gem"></i> 5号专家
</a>
<a href="{{ url_for('main.expert_generate') }}" class="btn-expert">
<i class="fas fa-brain"></i> 专家模式
</a>
<a href="/admin/apikeys" class="btn-expert" style="background:#64748b;">
<i class="fas fa-key"></i> API Key
</a>
</div>
</div>
@@ -77,6 +71,22 @@
<div class="result-content" id="optimizedPrompt"></div>
</div>
</div>
<div class="site-footer">
<div class="footer-group">
<span class="footer-label">专家工具</span>
<a href="{{ url_for('main.expert_generate') }}">1号专家原版</a>
<a href="{{ url_for('expert_generate_5.expert_generate_5_page') }}">5号专家含样例</a>
<a href="{{ url_for('expert_generate_6.expert_generate_6_page') }}">6号专家纯提示词</a>
</div>
<div class="footer-divider"></div>
<div class="footer-group">
<span class="footer-label">API 服务</span>
<a href="/api-docs">API 文档</a>
<a href="/api-key/apply">申请 API Key</a>
<a href="/admin/apikeys">管理 API Key</a>
</div>
</div>
</div>
</div>
@@ -319,10 +329,24 @@
.toast.success { background: #10b981; }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
/* Footer nav */
.site-footer {
display: flex; justify-content: center; align-items: flex-start;
gap: 2rem; margin-top: 3rem; padding-top: 1.5rem;
border-top: 1px solid var(--border-color);
}
.footer-group { display: flex; flex-direction: column; gap: 0.35rem; }
.footer-label { font-size: 0.72rem; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 0.25rem; }
.footer-group a { font-size: 0.85rem; color: var(--text-secondary); text-decoration: none; transition: color 0.2s; }
.footer-group a:hover { color: #06b6d4; }
.footer-divider { width: 1px; background: var(--border-color); align-self: stretch; }
@media (max-width: 768px) {
.main-layout { padding: 1rem; }
.content-header { flex-direction: column; align-items: flex-start; gap: 0.75rem; }
.disamb-grid { grid-template-columns: 1fr; }
.site-footer { flex-direction: column; gap: 1rem; }
.footer-divider { width: auto; height: 1px; }
}
</style>
{% endblock %}