优化数据
This commit is contained in:
Binary file not shown.
@@ -1,3 +1,7 @@
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from src.flask_prompt_master import create_app, db
|
||||
from src.flask_prompt_master.models import PromptTemplate
|
||||
import pymysql
|
||||
@@ -3622,22 +3626,42 @@ templates = [
|
||||
}
|
||||
]
|
||||
|
||||
def init_db():
|
||||
"""初始化数据库,保留现有user和prompt表数据"""
|
||||
def init_db(database_type='local'):
|
||||
"""
|
||||
初始化数据库,支持本地和腾讯云数据库
|
||||
|
||||
Args:
|
||||
database_type (str): 数据库类型,可选 'local' 或 'tencent'
|
||||
"""
|
||||
app = create_app()
|
||||
with app.app_context():
|
||||
try:
|
||||
# 连接数据库
|
||||
conn = pymysql.connect(
|
||||
host='localhost',
|
||||
user='root',
|
||||
password='123456',
|
||||
database='food_db',
|
||||
charset='utf8mb4'
|
||||
)
|
||||
# 根据数据库类型选择连接配置
|
||||
if database_type == 'tencent':
|
||||
# 腾讯云数据库配置
|
||||
conn = pymysql.connect(
|
||||
host='gz-cynosdbmysql-grp-d26pzce5.sql.tencentcdb.com',
|
||||
port=24936,
|
||||
user='root',
|
||||
password='!Rjb12191',
|
||||
database='pro_db',
|
||||
charset='utf8mb4'
|
||||
)
|
||||
print("🔗 连接到腾讯云数据库...")
|
||||
else:
|
||||
# 本地数据库配置
|
||||
conn = pymysql.connect(
|
||||
host='localhost',
|
||||
user='root',
|
||||
password='123456',
|
||||
database='pro_db', # 修正数据库名
|
||||
charset='utf8mb4'
|
||||
)
|
||||
print("🔗 连接到本地数据库...")
|
||||
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 只创建 prompt_template 表
|
||||
# 创建 prompt_template 表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS prompt_template (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
@@ -3652,12 +3676,14 @@ def init_db():
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
""")
|
||||
print("✅ prompt_template 表创建/检查完成")
|
||||
|
||||
# 检查是否已有模板数据
|
||||
cursor.execute("SELECT COUNT(*) FROM prompt_template")
|
||||
count = cursor.fetchone()[0]
|
||||
|
||||
if count == 0:
|
||||
print("📝 开始插入模板数据...")
|
||||
# 插入模板数据
|
||||
sql = """
|
||||
INSERT INTO prompt_template
|
||||
@@ -3665,28 +3691,35 @@ def init_db():
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
|
||||
"""
|
||||
|
||||
success_count = 0
|
||||
for template in templates: # templates 变量来自同文件中的模板列表
|
||||
cursor.execute(sql, (
|
||||
template['name'],
|
||||
template['description'],
|
||||
template.get('category', ''),
|
||||
template.get('industry', ''),
|
||||
template.get('profession', ''),
|
||||
template.get('sub_category', ''),
|
||||
template['system_prompt'],
|
||||
template.get('is_default', False)
|
||||
))
|
||||
try:
|
||||
cursor.execute(sql, (
|
||||
template['name'],
|
||||
template['description'],
|
||||
template.get('category', ''),
|
||||
template.get('industry', ''),
|
||||
template.get('profession', ''),
|
||||
template.get('sub_category', ''),
|
||||
template['system_prompt'],
|
||||
template.get('is_default', False)
|
||||
))
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
print(f"⚠️ 插入模板 '{template['name']}' 失败: {str(e)}")
|
||||
|
||||
print("模板数据初始化完成!")
|
||||
print(f"✅ 成功插入 {success_count} 个模板数据!")
|
||||
else:
|
||||
print("模板数据已存在,跳过初始化。")
|
||||
print(f"ℹ️ 模板数据已存在 ({count} 条记录),跳过初始化。")
|
||||
|
||||
# 提交事务
|
||||
conn.commit()
|
||||
print("数据库初始化完成!")
|
||||
print("🎉 数据库初始化完成!")
|
||||
|
||||
except Exception as e:
|
||||
print(f"初始化数据库失败: {str(e)}")
|
||||
print(f"❌ 初始化数据库失败: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
if 'conn' in locals():
|
||||
conn.rollback()
|
||||
finally:
|
||||
@@ -3695,5 +3728,36 @@ def init_db():
|
||||
if 'conn' in locals():
|
||||
conn.close()
|
||||
|
||||
|
||||
def init_tencent_db():
|
||||
"""专门用于初始化腾讯云数据库的便捷函数"""
|
||||
print("🚀 开始初始化腾讯云数据库...")
|
||||
init_db('tencent')
|
||||
|
||||
|
||||
def init_local_db():
|
||||
"""专门用于初始化本地数据库的便捷函数"""
|
||||
print("🚀 开始初始化本地数据库...")
|
||||
init_db('local')
|
||||
|
||||
if __name__ == '__main__':
|
||||
init_db()
|
||||
import sys
|
||||
|
||||
# 支持命令行参数选择数据库类型
|
||||
if len(sys.argv) > 1:
|
||||
db_type = sys.argv[1].lower()
|
||||
if db_type in ['tencent', 't']:
|
||||
init_tencent_db()
|
||||
elif db_type in ['local', 'l']:
|
||||
init_local_db()
|
||||
else:
|
||||
print("❌ 无效的数据库类型参数")
|
||||
print("用法: python promptsTemplates.py [local|tencent]")
|
||||
print(" local 或 l - 初始化本地数据库")
|
||||
print(" tencent 或 t - 初始化腾讯云数据库")
|
||||
sys.exit(1)
|
||||
else:
|
||||
# 默认初始化本地数据库
|
||||
print("ℹ️ 未指定数据库类型,默认初始化本地数据库")
|
||||
print("💡 提示:可以使用 'python promptsTemplates.py tencent' 初始化腾讯云数据库")
|
||||
init_local_db()
|
||||
Binary file not shown.
Binary file not shown.
@@ -132,6 +132,9 @@ def index():
|
||||
template_id = request.form.get('template_id')
|
||||
generated_text = generate_with_llm(form.input_text.data, template_id)
|
||||
|
||||
# 获取搜索状态
|
||||
search_state = request.form.get('search_state', '')
|
||||
|
||||
# 获取默认用户的 uid
|
||||
try:
|
||||
conn = pymysql.connect(
|
||||
@@ -164,7 +167,8 @@ def index():
|
||||
return render_template('generate.html', form=form, prompt=prompt, templates=templates,
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
professions=professions, categories=categories,
|
||||
sub_categories=sub_categories, selected_template_id=template_id)
|
||||
sub_categories=sub_categories, selected_template_id=template_id,
|
||||
search_state=search_state)
|
||||
return render_template('generate.html', form=form, prompt=None, templates=templates,
|
||||
get_template_icon=get_template_icon, industries=industries,
|
||||
professions=professions, categories=categories,
|
||||
|
||||
Binary file not shown.
@@ -20,8 +20,11 @@
|
||||
<i class="fas fa-brain"></i> 专家模式
|
||||
</a>
|
||||
</div>
|
||||
<form method="POST" class="generate-form">
|
||||
<form method="POST" class="generate-form" id="generateForm">
|
||||
{{ form.hidden_tag() }}
|
||||
<!-- 隐藏字段保存搜索状态 -->
|
||||
<input type="hidden" id="searchState" name="search_state" value="{{ search_state or '' }}">
|
||||
<input type="hidden" id="filterState" name="filter_state" value="">
|
||||
|
||||
<div class="search-section">
|
||||
<div class="search-box">
|
||||
@@ -131,7 +134,7 @@
|
||||
</form>
|
||||
|
||||
{% if prompt %}
|
||||
<div class="result-section">
|
||||
<div class="result-section animate-fade-in">
|
||||
<div class="result-card">
|
||||
<div class="result-header">
|
||||
<h2>生成结果</h2>
|
||||
@@ -819,6 +822,353 @@
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ==================== 动画效果 ==================== */
|
||||
|
||||
/* 淡入动画 */
|
||||
.animate-fade-in {
|
||||
animation: fadeIn 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 滑入动画 */
|
||||
.animate-slide-in {
|
||||
animation: slideIn 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 缩放动画 */
|
||||
.animate-scale-in {
|
||||
animation: scaleIn 0.4s ease-out;
|
||||
}
|
||||
|
||||
@keyframes scaleIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 结果卡片动画 */
|
||||
.result-section {
|
||||
animation: slideInUp 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(40px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 结果卡片内容动画 */
|
||||
.result-card {
|
||||
animation: fadeInScale 0.8s ease-out 0.2s both;
|
||||
}
|
||||
|
||||
@keyframes fadeInScale {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95) translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮悬停动画 */
|
||||
.btn {
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* 按钮点击波纹效果 */
|
||||
.btn::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
transform: translate(-50%, -50%);
|
||||
transition: width 0.6s, height 0.6s;
|
||||
}
|
||||
|
||||
.btn:active::before {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
/* 生成按钮特殊动画 */
|
||||
.btn-generate {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.btn-generate.generating {
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
box-shadow: 0 0 0 0 rgba(74, 144, 226, 0.7);
|
||||
}
|
||||
70% {
|
||||
box-shadow: 0 0 0 10px rgba(74, 144, 226, 0);
|
||||
}
|
||||
100% {
|
||||
box-shadow: 0 0 0 0 rgba(74, 144, 226, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 模板卡片悬停动画 */
|
||||
.template-card {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.template-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* 搜索框焦点动画 */
|
||||
.search-input:focus {
|
||||
animation: focusGlow 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes focusGlow {
|
||||
from {
|
||||
box-shadow: 0 0 0 0 rgba(74, 144, 226, 0.4);
|
||||
}
|
||||
to {
|
||||
box-shadow: 0 0 0 4px rgba(74, 144, 226, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
.loading-spinner {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: #fff;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 成功提示动画 */
|
||||
.success-message {
|
||||
animation: slideInDown 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes slideInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 错误提示动画 */
|
||||
.error-message {
|
||||
animation: shake 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%, 100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
25% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
75% {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 复制成功动画 */
|
||||
.copy-success {
|
||||
animation: bounce 0.6s ease-out;
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%, 20%, 50%, 80%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
40% {
|
||||
transform: translateY(-10px);
|
||||
}
|
||||
60% {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
}
|
||||
|
||||
/* 响应式动画优化 */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
animation-duration: 0.01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
transition-duration: 0.01ms !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 高亮文本动画 */
|
||||
.highlight {
|
||||
background: linear-gradient(120deg, #a8edea 0%, #fed6e3 100%);
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
animation: highlightPulse 0.5s ease-out;
|
||||
}
|
||||
|
||||
@keyframes highlightPulse {
|
||||
from {
|
||||
background: #ffeb3b;
|
||||
}
|
||||
to {
|
||||
background: linear-gradient(120deg, #a8edea 0%, #fed6e3 100%);
|
||||
}
|
||||
}
|
||||
|
||||
/* 搜索框聚焦状态 */
|
||||
.search-focused {
|
||||
transform: scale(1.02);
|
||||
box-shadow: 0 4px 20px rgba(74, 144, 226, 0.2);
|
||||
}
|
||||
|
||||
/* 筛选器变化动画 */
|
||||
.filter-changed {
|
||||
animation: filterChange 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes filterChange {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* 加载状态样式 */
|
||||
.generating {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.generating::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
animation: shimmer 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
left: -100%;
|
||||
}
|
||||
100% {
|
||||
left: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 结果内容动画 */
|
||||
.result-content {
|
||||
transition: all 0.6s ease-out;
|
||||
}
|
||||
|
||||
/* 模板卡片加载动画 */
|
||||
.template-card {
|
||||
transition: all 0.5s ease-out;
|
||||
}
|
||||
|
||||
/* 按钮组动画 */
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
animation: slideInUp 0.6s ease-out 0.3s both;
|
||||
}
|
||||
|
||||
/* 表单动画 */
|
||||
.generate-form {
|
||||
animation: fadeIn 0.8s ease-out;
|
||||
}
|
||||
|
||||
/* 搜索区域动画 */
|
||||
.search-section {
|
||||
animation: slideInDown 0.6s ease-out 0.1s both;
|
||||
}
|
||||
|
||||
/* 模板区域动画 */
|
||||
.templates-section {
|
||||
animation: fadeIn 0.8s ease-out 0.2s both;
|
||||
}
|
||||
|
||||
/* 输入区域动画 */
|
||||
.input-section {
|
||||
animation: slideInUp 0.6s ease-out 0.4s both;
|
||||
}
|
||||
|
||||
/* 操作区域动画 */
|
||||
.action-section {
|
||||
animation: slideInUp 0.6s ease-out 0.5s both;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -1274,6 +1624,341 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
firstTemplate.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复搜索状态
|
||||
restoreSearchState();
|
||||
});
|
||||
|
||||
// 保存搜索状态
|
||||
function saveSearchState() {
|
||||
const searchTerm = document.getElementById('templateSearch').value;
|
||||
const industryFilter = document.getElementById('industryFilter').value;
|
||||
const professionFilter = document.getElementById('professionFilter').value;
|
||||
const subCategoryFilter = document.getElementById('subCategoryFilter').value;
|
||||
const activeTab = document.querySelector('.tab.active')?.dataset.category || 'all';
|
||||
|
||||
const searchState = {
|
||||
searchTerm: searchTerm,
|
||||
industry: industryFilter,
|
||||
profession: professionFilter,
|
||||
subCategory: subCategoryFilter,
|
||||
activeTab: activeTab
|
||||
};
|
||||
|
||||
console.log('保存搜索状态:', searchState);
|
||||
document.getElementById('searchState').value = JSON.stringify(searchState);
|
||||
console.log('搜索状态已保存到隐藏字段:', document.getElementById('searchState').value);
|
||||
}
|
||||
|
||||
// 恢复搜索状态
|
||||
function restoreSearchState() {
|
||||
const searchStateValue = document.getElementById('searchState').value;
|
||||
console.log('搜索状态值:', searchStateValue);
|
||||
|
||||
if (searchStateValue) {
|
||||
try {
|
||||
const searchState = JSON.parse(searchStateValue);
|
||||
console.log('解析的搜索状态:', searchState);
|
||||
|
||||
// 恢复搜索框
|
||||
if (searchState.searchTerm) {
|
||||
document.getElementById('templateSearch').value = searchState.searchTerm;
|
||||
console.log('恢复搜索词:', searchState.searchTerm);
|
||||
}
|
||||
|
||||
// 恢复筛选器
|
||||
if (searchState.industry) {
|
||||
document.getElementById('industryFilter').value = searchState.industry;
|
||||
console.log('恢复行业筛选:', searchState.industry);
|
||||
}
|
||||
if (searchState.profession) {
|
||||
document.getElementById('professionFilter').value = searchState.profession;
|
||||
console.log('恢复职业筛选:', searchState.profession);
|
||||
}
|
||||
if (searchState.subCategory) {
|
||||
document.getElementById('subCategoryFilter').value = searchState.subCategory;
|
||||
console.log('恢复子分类筛选:', searchState.subCategory);
|
||||
}
|
||||
|
||||
// 恢复活动标签
|
||||
if (searchState.activeTab) {
|
||||
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
|
||||
const activeTab = document.querySelector(`[data-category="${searchState.activeTab}"]`);
|
||||
if (activeTab) {
|
||||
activeTab.classList.add('active');
|
||||
console.log('恢复活动标签:', searchState.activeTab);
|
||||
}
|
||||
}
|
||||
|
||||
// 手动执行搜索和筛选逻辑
|
||||
setTimeout(() => {
|
||||
console.log('开始执行搜索和筛选...');
|
||||
performSearchAndFilter();
|
||||
}, 100);
|
||||
|
||||
} catch (e) {
|
||||
console.error('恢复搜索状态失败:', e);
|
||||
}
|
||||
} else {
|
||||
console.log('没有搜索状态需要恢复');
|
||||
}
|
||||
}
|
||||
|
||||
// 执行搜索和筛选的综合函数
|
||||
function performSearchAndFilter() {
|
||||
const searchTerm = document.getElementById('templateSearch').value.toLowerCase().trim();
|
||||
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 || 'all';
|
||||
|
||||
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 cardIndustry = card.dataset.industry.toLowerCase();
|
||||
const cardProfession = card.dataset.profession.toLowerCase();
|
||||
const cardCategory = card.dataset.category.toLowerCase();
|
||||
const cardSubCategory = card.dataset.subcategory.toLowerCase();
|
||||
|
||||
// 检查搜索匹配
|
||||
const matchSearch = !searchTerm ||
|
||||
name.includes(searchTerm) ||
|
||||
description.includes(searchTerm) ||
|
||||
cardIndustry.includes(searchTerm) ||
|
||||
cardProfession.includes(searchTerm) ||
|
||||
cardCategory.includes(searchTerm) ||
|
||||
cardSubCategory.includes(searchTerm);
|
||||
|
||||
// 检查筛选匹配
|
||||
const matchIndustry = industry === 'all' || cardIndustry === industry.toLowerCase();
|
||||
const matchProfession = profession === 'all' || cardProfession === profession.toLowerCase();
|
||||
const matchSubCategory = subCategory === 'all' || cardSubCategory === subCategory.toLowerCase();
|
||||
const matchCategory = category === 'all' || cardCategory === category.toLowerCase();
|
||||
|
||||
const matchFilters = matchIndustry && matchProfession && matchSubCategory && matchCategory;
|
||||
|
||||
if (matchSearch && matchFilters) {
|
||||
card.style.display = 'block';
|
||||
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';
|
||||
}
|
||||
});
|
||||
|
||||
// 更新统计信息
|
||||
const searchStats = document.querySelector('.search-count');
|
||||
if (searchStats) {
|
||||
searchStats.textContent = `显示 ${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', () => {
|
||||
document.getElementById('templateSearch').value = '';
|
||||
document.getElementById('industryFilter').value = 'all';
|
||||
document.getElementById('professionFilter').value = 'all';
|
||||
document.getElementById('subCategoryFilter').value = 'all';
|
||||
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
|
||||
document.querySelector('[data-category="all"]').classList.add('active');
|
||||
performSearchAndFilter();
|
||||
});
|
||||
}
|
||||
} else if (noResultsEl) {
|
||||
noResultsEl.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// 高亮文本函数
|
||||
function highlightText(text, searchTerm) {
|
||||
if (!searchTerm) return text;
|
||||
const regex = new RegExp(`(${searchTerm})`, 'gi');
|
||||
return text.replace(regex, '<span class="highlight">$1</span>');
|
||||
}
|
||||
|
||||
// 表单提交前保存搜索状态
|
||||
document.getElementById('generateForm').addEventListener('submit', function() {
|
||||
saveSearchState();
|
||||
|
||||
// 添加生成按钮动画效果
|
||||
const generateBtn = document.querySelector('.btn-generate');
|
||||
generateBtn.classList.add('generating');
|
||||
generateBtn.innerHTML = '<span class="loading-spinner"></span> 生成中...';
|
||||
generateBtn.disabled = true;
|
||||
});
|
||||
|
||||
// 增强的复制功能
|
||||
function copyText(text) {
|
||||
if (navigator.clipboard && window.isSecureContext) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showCopySuccess();
|
||||
}).catch(() => {
|
||||
fallbackCopyText(text);
|
||||
});
|
||||
} else {
|
||||
fallbackCopyText(text);
|
||||
}
|
||||
}
|
||||
|
||||
// 备用复制方法
|
||||
function fallbackCopyText(text) {
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-999999px';
|
||||
textArea.style.top = '-999999px';
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
showCopySuccess();
|
||||
} catch (err) {
|
||||
console.error('复制失败:', err);
|
||||
showCopyError();
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
// 显示复制成功动画
|
||||
function showCopySuccess() {
|
||||
const copyBtn = document.querySelector('.btn-copy');
|
||||
const originalText = copyBtn.innerHTML;
|
||||
|
||||
copyBtn.classList.add('copy-success');
|
||||
copyBtn.innerHTML = '<i class="fas fa-check"></i> 已复制';
|
||||
copyBtn.style.background = '#4CAF50';
|
||||
|
||||
setTimeout(() => {
|
||||
copyBtn.classList.remove('copy-success');
|
||||
copyBtn.innerHTML = originalText;
|
||||
copyBtn.style.background = '';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 显示复制错误
|
||||
function showCopyError() {
|
||||
const copyBtn = document.querySelector('.btn-copy');
|
||||
const originalText = copyBtn.innerHTML;
|
||||
|
||||
copyBtn.innerHTML = '<i class="fas fa-times"></i> 复制失败';
|
||||
copyBtn.style.background = '#f44336';
|
||||
|
||||
setTimeout(() => {
|
||||
copyBtn.innerHTML = originalText;
|
||||
copyBtn.style.background = '';
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// 添加页面加载动画
|
||||
function addPageLoadAnimations() {
|
||||
// 为模板卡片添加延迟动画
|
||||
const templateCards = document.querySelectorAll('.template-card');
|
||||
templateCards.forEach((card, index) => {
|
||||
card.style.opacity = '0';
|
||||
card.style.transform = 'translateY(20px)';
|
||||
|
||||
setTimeout(() => {
|
||||
card.style.transition = 'all 0.5s ease-out';
|
||||
card.style.opacity = '1';
|
||||
card.style.transform = 'translateY(0)';
|
||||
}, index * 50); // 每个卡片延迟50ms
|
||||
});
|
||||
}
|
||||
|
||||
// 添加搜索框动画
|
||||
function addSearchAnimations() {
|
||||
const searchInput = document.getElementById('templateSearch');
|
||||
|
||||
searchInput.addEventListener('focus', function() {
|
||||
this.parentElement.classList.add('search-focused');
|
||||
});
|
||||
|
||||
searchInput.addEventListener('blur', function() {
|
||||
this.parentElement.classList.remove('search-focused');
|
||||
});
|
||||
}
|
||||
|
||||
// 添加筛选器动画
|
||||
function addFilterAnimations() {
|
||||
const filters = document.querySelectorAll('.custom-select');
|
||||
|
||||
filters.forEach(filter => {
|
||||
filter.addEventListener('change', function() {
|
||||
this.classList.add('filter-changed');
|
||||
setTimeout(() => {
|
||||
this.classList.remove('filter-changed');
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 添加结果区域动画
|
||||
function addResultAnimations() {
|
||||
const resultSection = document.querySelector('.result-section');
|
||||
if (resultSection) {
|
||||
// 滚动到结果区域
|
||||
resultSection.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
|
||||
// 添加结果内容动画
|
||||
const resultContent = resultSection.querySelector('.result-content');
|
||||
if (resultContent) {
|
||||
resultContent.style.opacity = '0';
|
||||
resultContent.style.transform = 'translateY(20px)';
|
||||
|
||||
setTimeout(() => {
|
||||
resultContent.style.transition = 'all 0.6s ease-out';
|
||||
resultContent.style.opacity = '1';
|
||||
resultContent.style.transform = 'translateY(0)';
|
||||
}, 200);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化所有动画
|
||||
function initializeAnimations() {
|
||||
addPageLoadAnimations();
|
||||
addSearchAnimations();
|
||||
addFilterAnimations();
|
||||
|
||||
// 如果有结果区域,添加结果动画
|
||||
if (document.querySelector('.result-section')) {
|
||||
addResultAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后初始化动画
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 延迟初始化动画,确保页面完全加载
|
||||
setTimeout(initializeAnimations, 100);
|
||||
});
|
||||
|
||||
// 收藏功能
|
||||
|
||||
Reference in New Issue
Block a user