Files
aitsc/flask_prompt_master/templates/generate.html

1235 lines
35 KiB
HTML

{% extends "base.html" %}
{% block title %}提示词大师{% endblock %}
{% block content %}
<div class="hero">
<div class="hero-content">
<h1>提示词大师</h1>
<p class="subtitle">让AI更好地理解您的需求</p>
<div class="hero-decoration"></div>
</div>
</div>
<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>提示词生成器</h2>
<a href="{{ url_for('main.expert_generate') }}" class="btn btn-primary">
<i class="fas fa-brain"></i> 专家模式
</a>
</div>
<form method="POST" class="generate-form">
{{ form.hidden_tag() }}
<div class="search-section">
<div class="search-box">
<input type="text" id="templateSearch" class="search-input" placeholder="搜索模板名称或描述...">
<i class="fas fa-search search-icon"></i>
</div>
<div class="search-stats">
<span class="search-count"></span>
</div>
</div>
<div class="templates-section">
<div class="section-header">
<h2>选择场景</h2>
<div class="filter-controls">
<select id="industryFilter" class="custom-select">
<option value="all">全部行业</option>
{% for industry in industries %}
<option value="{{ industry }}">{{ industry }}</option>
{% endfor %}
</select>
<select id="professionFilter" class="custom-select">
<option value="all">全部职业</option>
{% for profession in professions %}
<option value="{{ profession }}">{{ profession }}</option>
{% endfor %}
</select>
<select id="subCategoryFilter" class="custom-select">
<option value="all">全部领域</option>
{% for sub_category in sub_categories %}
<option value="{{ sub_category }}">{{ sub_category }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="category-tabs">
<div class="tab active" data-category="all">全部</div>
{% for category in categories %}
<div class="tab" data-category="{{ category }}">{{ category }}</div>
{% endfor %}
</div>
<div class="template-grid">
{% for template in templates %}
<div class="template-card"
data-category="{{ template.category }}"
data-industry="{{ template.industry }}"
data-profession="{{ template.profession }}"
data-subcategory="{{ template.sub_category }}">
<input type="radio" name="template_id" id="template_{{ template.id }}"
value="{{ template.id }}"
{% if template.is_default %}checked{% endif %}>
<label for="template_{{ template.id }}" class="template-content">
<div class="template-actions">
<button type="button" class="btn-delete"
data-template-id="{{ template.id }}"
title="删除模板">
<i class="fas fa-trash-alt"></i>
</button>
</div>
<div class="template-categories">
<div class="category-group">
<span class="category-label">行业:</span>
<span class="category-value">{{ template.industry }}</span>
</div>
<div class="category-group">
<span class="category-label">职业:</span>
<span class="category-value">{{ template.profession }}</span>
</div>
<div class="category-group">
<span class="category-label">领域:</span>
<span class="category-value">{{ template.sub_category }}</span>
</div>
</div>
<div class="template-header">
<div class="template-icon">
<i class="fas {{ get_template_icon(template.category) }}"></i>
</div>
<h3>{{ template.name }}</h3>
</div>
<div class="template-info">
<p>{{ template.description }}</p>
</div>
</label>
</div>
{% endfor %}
</div>
</div>
<div class="input-section">
<div class="section-header">
<h2>输入需求</h2>
<span class="subtitle">描述越详细,生成的提示词效果越好</span>
</div>
{{ form.input_text(class="form-control custom-textarea", rows=5,
placeholder="请详细描述您的需求...") }}
</div>
<div class="action-section">
{{ form.submit(class="btn btn-primary btn-generate", value="生成专业提示词") }}
</div>
</form>
{% if prompt %}
<div class="result-section">
<div class="result-card">
<div class="result-header">
<h2>生成结果</h2>
<div class="result-actions">
<button class="btn btn-copy" onclick="copyText('{{ prompt.generated_text }}')">
<i class="fas fa-copy"></i>
复制提示词
</button>
</div>
</div>
<div class="result-content">
<div class="output-preview">
<div class="text-content">{{ prompt.generated_text }}</div>
</div>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
<div class="filter-stats">
<div class="stat-item">
<span class="stat-label">行业:</span>
<span class="stat-value">{{ industries|length }}</span>
</div>
<div class="stat-item">
<span class="stat-label">职业:</span>
<span class="stat-value">{{ professions|length }}</span>
</div>
<div class="stat-item">
<span class="stat-label">领域:</span>
<span class="stat-value">{{ sub_categories|length }}</span>
</div>
</div>
<div id="deleteModal" class="modal">
<div class="modal-content">
<h3>确认删除</h3>
<p>确定要删除这个提示词模板吗?此操作无法撤销。</p>
<div class="modal-actions">
<button class="btn-cancel">取消</button>
<button class="btn-confirm">确认删除</button>
</div>
</div>
</div>
<style>
:root {
--primary-color: #4a90e2;
--primary-dark: #357abd;
--secondary-color: #f5f7fa;
--text-color: #2c3e50;
--border-color: #e0e0e0;
--hover-color: #e8f0fe;
--success-color: #4CAF50;
--success-dark: #45a049;
}
.hero {
background: linear-gradient(135deg, var(--primary-color), #6aa9f7);
padding: 4rem 0;
margin-bottom: 3rem;
position: relative;
overflow: hidden;
}
.hero-content {
text-align: center;
color: white;
position: relative;
z-index: 1;
}
.hero h1 {
font-size: 3rem;
margin-bottom: 1rem;
font-weight: 700;
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.hero .subtitle {
font-size: 1.2rem;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
.hero-decoration {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM12 60c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z' fill='%23ffffff' fill-opacity='0.1' fill-rule='evenodd'/%3E%3C/svg%3E");
opacity: 0.1;
}
.main-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.section-header h2 {
font-size: 1.5rem;
color: var(--text-color);
margin: 0;
}
.filter-controls {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
}
.custom-select {
padding: 0.5rem 1rem;
border: 1px solid var(--border-color);
border-radius: 6px;
background: white;
min-width: 150px;
}
.category-tabs {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
border-bottom: 1px solid var(--border-color);
padding-bottom: 0.5rem;
}
.tab {
padding: 0.5rem 1rem;
cursor: pointer;
border-radius: 6px;
transition: all 0.3s ease;
}
.tab.active {
background: var(--primary-color);
color: white;
}
.template-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 1.5rem;
margin-bottom: 2rem;
}
.template-card {
position: relative;
transition: transform 0.2s, box-shadow 0.2s;
}
.template-content {
display: flex;
padding: 1.5rem;
background: white;
border: 1px solid var(--border-color);
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
height: 100%;
}
.template-content:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
border-color: var(--primary-color);
}
.template-icon {
font-size: 1.5rem;
color: var(--primary-color);
margin-right: 1rem;
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
background: var(--hover-color);
border-radius: 12px;
}
.template-info {
flex: 1;
}
.template-info h3 {
margin: 0 0 0.5rem;
font-size: 1.1rem;
color: var(--text-color);
}
.template-info p {
margin: 0 0 1rem;
font-size: 0.9rem;
color: #666;
line-height: 1.5;
}
.template-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
}
.template-tags {
display: flex;
gap: 0.5rem;
margin-bottom: 0.5rem;
}
.tag {
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
border-radius: 4px;
white-space: nowrap;
}
.tag.industry {
background: #e3f2fd;
color: #1976d2;
}
.tag.profession {
background: #f3e5f5;
color: #7b1fa2;
}
.subcategory-tag {
display: inline-block;
margin-top: 0.5rem;
padding: 0.25rem 0.75rem;
background: #f5f5f5;
border-radius: 20px;
font-size: 0.8rem;
color: #666;
}
.custom-textarea {
width: 100%;
padding: 1rem;
border: 1px solid var(--border-color);
border-radius: 12px;
resize: vertical;
min-height: 120px;
font-size: 1rem;
line-height: 1.6;
transition: border-color 0.3s;
}
.custom-textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
}
.btn-generate {
background: var(--primary-color);
color: white;
padding: 1rem 2rem;
font-size: 1.1rem;
border-radius: 8px;
border: none;
cursor: pointer;
transition: all 0.3s ease;
width: 100%;
max-width: 300px;
margin: 2rem auto;
display: block;
}
.btn-generate:hover {
background: var(--primary-dark);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(74, 144, 226, 0.2);
}
.result-card {
background: white;
border-radius: 16px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
padding: 2rem;
margin-top: 2rem;
animation: slideUp 0.5s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-color);
}
.text-content {
background: var(--secondary-color);
padding: 1.5rem;
border-radius: 12px;
white-space: pre-wrap;
font-size: 1rem;
line-height: 1.6;
color: var(--text-color);
}
.filter-stats {
display: flex;
gap: 2rem;
margin: 1rem 0;
padding: 1rem;
background: #f8f9fa;
border-radius: 8px;
}
.stat-item {
display: flex;
align-items: center;
gap: 0.5rem;
}
.stat-label {
color: #666;
font-size: 0.9rem;
}
.stat-value {
font-weight: bold;
color: var(--primary-color);
}
.template-categories {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 12px;
padding: 8px;
background: #f8f9fa;
border-radius: 6px;
}
.category-group {
display: flex;
align-items: center;
gap: 4px;
padding: 4px 8px;
background: white;
border-radius: 4px;
font-size: 0.85rem;
}
.category-label {
color: #666;
font-weight: 500;
}
.category-value {
color: var(--primary-color);
font-weight: 600;
}
.template-card {
background: white;
border: 1px solid var(--border-color);
border-radius: 12px;
transition: all 0.3s ease;
}
.template-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
border-color: var(--primary-color);
}
.template-content {
padding: 16px;
cursor: pointer;
display: block;
}
.template-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.template-header h3 {
margin: 0;
font-size: 1.1rem;
color: var(--text-color);
}
.template-info p {
margin: 0;
font-size: 0.9rem;
color: #666;
line-height: 1.5;
}
.filter-active {
background: var(--primary-color);
color: white;
}
@media (max-width: 768px) {
.hero {
padding: 3rem 1rem;
}
.hero h1 {
font-size: 2rem;
}
.main-container {
padding: 0 1rem;
}
.template-grid {
grid-template-columns: 1fr;
}
.section-header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.result-header {
flex-direction: column;
gap: 1rem;
}
.btn-generate {
width: 100%;
}
.filter-controls {
flex-direction: column;
gap: 0.5rem;
}
.category-tabs {
overflow-x: auto;
padding-bottom: 0.5rem;
}
.tab {
white-space: nowrap;
}
.filter-stats {
flex-direction: column;
gap: 1rem;
}
.template-tags {
flex-wrap: wrap;
}
.template-categories {
flex-direction: column;
}
.category-group {
width: 100%;
}
}
.filter-active {
border-color: var(--primary-color);
background-color: var(--hover-color);
}
.no-results {
width: 100%;
padding: 3rem;
text-align: center;
}
.no-results-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.no-results i {
font-size: 2rem;
color: #999;
}
.no-results h3 {
margin: 0;
color: #666;
}
.no-results p {
margin: 0;
color: #999;
}
.btn-reset-filters {
padding: 0.5rem 1rem;
background: var(--primary-color);
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-reset-filters:hover {
background: var(--primary-dark);
}
.filter-stats {
background: white;
border: 1px solid var(--border-color);
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
}
.stat-item {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.25rem 0.75rem;
background: var(--secondary-color);
border-radius: 4px;
margin-right: 0.5rem;
}
.search-section {
margin-bottom: 1.5rem;
}
.search-box {
position: relative;
max-width: 600px;
margin: 0 auto;
}
.search-input {
width: 100%;
padding: 12px 40px 12px 16px;
border: 2px solid var(--border-color);
border-radius: 8px;
font-size: 1rem;
transition: all 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.1);
}
.search-icon {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
color: #999;
pointer-events: none;
}
.search-stats {
margin-top: 0.5rem;
text-align: center;
font-size: 0.9rem;
color: #666;
}
.highlight {
background-color: rgba(255, 247, 0, 0.3);
padding: 0 2px;
border-radius: 2px;
}
@media (max-width: 768px) {
.search-box {
margin: 0;
}
}
.template-actions {
position: absolute;
top: 1rem;
right: 1rem;
opacity: 0;
transition: opacity 0.3s ease;
}
.template-card:hover .template-actions {
opacity: 1;
}
.btn-delete {
padding: 0.5rem;
background: none;
border: none;
color: #dc3545;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-delete:hover {
color: #c82333;
transform: scale(1.1);
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
}
.modal-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 2rem;
border-radius: 8px;
min-width: 300px;
text-align: center;
}
.modal-actions {
display: flex;
justify-content: center;
gap: 1rem;
margin-top: 1.5rem;
}
.btn-cancel {
padding: 0.5rem 1.5rem;
background: #6c757d;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-confirm {
padding: 0.5rem 1.5rem;
background: #dc3545;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const industryFilter = document.getElementById('industryFilter');
const professionFilter = document.getElementById('professionFilter');
const subCategoryFilter = document.getElementById('subCategoryFilter');
const tabs = document.querySelectorAll('.tab');
// 更新筛选状态显示
function updateFilterStatus() {
const industry = industryFilter.value;
const profession = professionFilter.value;
const subCategory = subCategoryFilter.value;
// 更新选中状态样式
document.querySelectorAll('.filter-controls select').forEach(select => {
if (select.value !== 'all') {
select.classList.add('filter-active');
} else {
select.classList.remove('filter-active');
}
});
// 更新统计信息
const visibleCards = document.querySelectorAll('.template-card:not(.hidden)');
const totalCards = document.querySelectorAll('.template-card');
document.querySelector('.filter-stats').innerHTML = `
<div class="stat-item">
<span class="stat-label">当前筛选:</span>
<span class="stat-value">${visibleCards.length}/${totalCards.length}</span>
</div>
${industry !== 'all' ? `
<div class="stat-item">
<span class="stat-label">行业:</span>
<span class="stat-value">${industry}</span>
</div>
` : ''}
${profession !== 'all' ? `
<div class="stat-item">
<span class="stat-label">职业:</span>
<span class="stat-value">${profession}</span>
</div>
` : ''}
${subCategory !== 'all' ? `
<div class="stat-item">
<span class="stat-label">领域:</span>
<span class="stat-value">${subCategory}</span>
</div>
` : ''}
`;
}
// 改进的筛选逻辑
function filterTemplates() {
const industry = industryFilter.value;
const profession = professionFilter.value;
const subCategory = subCategoryFilter.value;
const category = document.querySelector('.tab.active').dataset.category;
const cards = document.querySelectorAll('.template-card');
let hasVisibleCards = false;
cards.forEach(card => {
const matchIndustry = industry === 'all' || card.dataset.industry === industry;
const matchProfession = profession === 'all' || card.dataset.profession === profession;
const matchSubCategory = subCategory === 'all' || card.dataset.subcategory === subCategory;
const matchCategory = category === 'all' || card.dataset.category === category;
if (matchIndustry && matchProfession && matchSubCategory && matchCategory) {
card.classList.remove('hidden');
card.style.display = '';
hasVisibleCards = true;
} else {
card.classList.add('hidden');
card.style.display = 'none';
}
});
// 显示无结果提示
const noResultsEl = document.querySelector('.no-results');
if (!hasVisibleCards) {
if (!noResultsEl) {
const noResults = document.createElement('div');
noResults.className = 'no-results';
noResults.innerHTML = `
<div class="no-results-content">
<i class="fas fa-search"></i>
<h3>未找到匹配的模板</h3>
<p>请尝试调整筛选条件</p>
<button class="btn-reset-filters">重置筛选</button>
</div>
`;
document.querySelector('.template-grid').appendChild(noResults);
// 绑定重置按钮事件
noResults.querySelector('.btn-reset-filters').addEventListener('click', resetFilters);
}
} else if (noResultsEl) {
noResultsEl.remove();
}
updateFilterStatus();
}
// 重置筛选条件
function resetFilters() {
industryFilter.value = 'all';
professionFilter.value = 'all';
subCategoryFilter.value = 'all';
document.querySelector('.tab.active').classList.remove('active');
document.querySelector('[data-category="all"]').classList.add('active');
filterTemplates();
}
// 绑定事件
[industryFilter, professionFilter, subCategoryFilter].forEach(filter => {
filter.addEventListener('change', filterTemplates);
});
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
filterTemplates();
});
});
// 初始化筛选
filterTemplates();
});
function copyText(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('.btn-copy');
if (copyBtn) {
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="fas fa-check"></i> 已复制';
copyBtn.classList.add('copied');
setTimeout(() => {
copyBtn.innerHTML = originalText;
copyBtn.classList.remove('copied');
}, 2000);
}
}
// 添加按钮样式
const style = document.createElement('style');
style.textContent = `
.btn-copy {
padding: 8px 16px;
background: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-copy:hover {
background: #45a049;
}
.btn-copy.copied {
background: #45a049;
}
.btn-copy i {
margin-right: 6px;
}
`;
document.head.appendChild(style);
// 确保在DOM加载完成后绑定事件
document.addEventListener('DOMContentLoaded', function() {
const copyButtons = document.querySelectorAll('.btn-copy');
copyButtons.forEach(button => {
button.addEventListener('click', function(e) {
e.preventDefault();
const text = this.closest('.result-card').querySelector('.text-content').textContent;
copyText(text);
});
});
});
// 添加搜索功能
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('templateSearch');
const searchStats = document.querySelector('.search-count');
function highlightText(text, searchTerm) {
if (!searchTerm) return text;
const regex = new RegExp(`(${searchTerm})`, 'gi');
return text.replace(regex, '<span class="highlight">$1</span>');
}
function updateSearchStats(visibleCount, totalCount) {
searchStats.textContent = `显示 ${visibleCount}/${totalCount} 个模板`;
}
function searchTemplates() {
const searchTerm = searchInput.value.toLowerCase().trim();
const cards = document.querySelectorAll('.template-card');
let visibleCount = 0;
cards.forEach(card => {
const name = card.querySelector('h3').textContent.toLowerCase();
const description = card.querySelector('p').textContent.toLowerCase();
const industry = card.dataset.industry.toLowerCase();
const profession = card.dataset.profession.toLowerCase();
const category = card.dataset.category.toLowerCase();
const subCategory = card.dataset.subcategory.toLowerCase();
const matchSearch = !searchTerm ||
name.includes(searchTerm) ||
description.includes(searchTerm) ||
industry.includes(searchTerm) ||
profession.includes(searchTerm) ||
category.includes(searchTerm) ||
subCategory.includes(searchTerm);
// 检查筛选条件
const matchFilters = checkFilters(card);
if (matchSearch && matchFilters) {
card.style.display = '';
visibleCount++;
// 高亮匹配文本
if (searchTerm) {
card.querySelector('h3').innerHTML = highlightText(card.querySelector('h3').textContent, searchTerm);
card.querySelector('p').innerHTML = highlightText(card.querySelector('p').textContent, searchTerm);
}
} else {
card.style.display = 'none';
}
});
updateSearchStats(visibleCount, cards.length);
// 显示无结果提示
const noResultsEl = document.querySelector('.no-results');
if (visibleCount === 0) {
if (!noResultsEl) {
const noResults = document.createElement('div');
noResults.className = 'no-results';
noResults.innerHTML = `
<div class="no-results-content">
<i class="fas fa-search"></i>
<h3>未找到匹配的模板</h3>
<p>请尝试其他搜索关键词</p>
<button class="btn-reset-search">清除搜索</button>
</div>
`;
document.querySelector('.template-grid').appendChild(noResults);
// 绑定清除搜索按钮事件
noResults.querySelector('.btn-reset-search').addEventListener('click', () => {
searchInput.value = '';
searchTemplates();
});
}
} else if (noResultsEl) {
noResultsEl.remove();
}
}
function checkFilters(card) {
const industry = document.getElementById('industryFilter').value;
const profession = document.getElementById('professionFilter').value;
const subCategory = document.getElementById('subCategoryFilter').value;
const category = document.querySelector('.tab.active').dataset.category;
return (industry === 'all' || card.dataset.industry === industry) &&
(profession === 'all' || card.dataset.profession === profession) &&
(subCategory === 'all' || card.dataset.subcategory === subCategory) &&
(category === 'all' || card.dataset.category === category);
}
// 防抖函数
function debounce(func, wait) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(context, args), wait);
};
}
// 绑定搜索事件
searchInput.addEventListener('input', debounce(searchTemplates, 300));
// 初始化搜索统计
updateSearchStats(document.querySelectorAll('.template-card').length,
document.querySelectorAll('.template-card').length);
});
// 添加删除功能相关的脚本
document.addEventListener('DOMContentLoaded', function() {
const modal = document.getElementById('deleteModal');
const btnCancel = modal.querySelector('.btn-cancel');
const btnConfirm = modal.querySelector('.btn-confirm');
let currentTemplateId = null;
// 显示删除确认框
function showDeleteModal(templateId) {
currentTemplateId = templateId;
modal.style.display = 'block';
}
// 隐藏删除确认框
function hideDeleteModal() {
modal.style.display = 'none';
currentTemplateId = null;
}
// 删除模板
async function deleteTemplate(templateId) {
try {
const response = await fetch(`/api/templates/${templateId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': document.querySelector('input[name="csrf_token"]').value
}
});
if (response.ok) {
// 删除成功,移除对应的模板卡片
const card = document.querySelector(`[data-template-id="${templateId}"]`)
.closest('.template-card');
card.remove();
// 更新统计信息
updateSearchStats(
document.querySelectorAll('.template-card:not(.hidden)').length,
document.querySelectorAll('.template-card').length
);
} else {
throw new Error('删除失败');
}
} catch (error) {
console.error('删除模板时出错:', error);
alert('删除模板失败,请稍后重试');
} finally {
hideDeleteModal();
}
}
// 绑定删除按钮点击事件
document.querySelectorAll('.btn-delete').forEach(btn => {
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
const templateId = btn.dataset.templateId;
showDeleteModal(templateId);
});
});
// 绑定取消按钮事件
btnCancel.addEventListener('click', hideDeleteModal);
// 绑定确认删除按钮事件
btnConfirm.addEventListener('click', () => {
if (currentTemplateId) {
deleteTemplate(currentTemplateId);
}
});
// 点击模态框外部关闭
modal.addEventListener('click', (e) => {
if (e.target === modal) {
hideDeleteModal();
}
});
});
</script>
{% endblock %}