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:
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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 %}
|
||||
|
||||
@@ -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">0–2,默认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">100–4000,默认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">10–300秒,默认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 %}
|
||||
|
||||
414
src/flask_prompt_master/templates/expert_generate_4.html
Normal file
414
src/flask_prompt_master/templates/expert_generate_4.html
Normal 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 %}
|
||||
@@ -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 %}
|
||||
|
||||
Reference in New Issue
Block a user