diff --git a/logs/app.log b/logs/app.log index 0735dfc..dc0a7e6 100644 --- a/logs/app.log +++ b/logs/app.log @@ -1642,3 +1642,4 @@ sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between pa 2025-10-10 23:48:15,536 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82] 2025-10-10 23:53:07,815 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82] 2025-10-11 00:01:46,182 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82] +2025-10-11 00:06:23,151 INFO: 应用启动 [in /home/renjianbo/aitsc/config/base.py:82] diff --git a/logs/gunicorn.pid b/logs/gunicorn.pid index b0effbb..423f4c0 100644 --- a/logs/gunicorn.pid +++ b/logs/gunicorn.pid @@ -1 +1 @@ -1906 +28527 diff --git a/logs/gunicorn_access.log b/logs/gunicorn_access.log index 0ec3127..0797e99 100644 --- a/logs/gunicorn_access.log +++ b/logs/gunicorn_access.log @@ -11538,3 +11538,19 @@ 127.0.0.1 - - [11/Oct/2025:00:03:03 +0800] "GET /static/css/style.css HTTP/1.1" 404 207 "-" "python-requests/2.31.0" 1290 127.0.0.1 - - [11/Oct/2025:00:03:10 +0800] "GET / HTTP/1.1" 200 1403956 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 658765 127.0.0.1 - - [11/Oct/2025:00:03:11 +0800] "GET /api/check-login HTTP/1.1" 200 35 "http://localhost:5002/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 4487 +127.0.0.1 - - [11/Oct/2025:00:06:29 +0800] "GET / HTTP/1.1" 200 1404048 "-" "python-requests/2.31.0" 568774 +127.0.0.1 - - [11/Oct/2025:00:06:29 +0800] "GET /static/js/interactions.js HTTP/1.1" 404 207 "-" "python-requests/2.31.0" 1301 +127.0.0.1 - - [11/Oct/2025:00:06:36 +0800] "GET / HTTP/1.1" 200 1404048 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 227041 +127.0.0.1 - - [11/Oct/2025:00:06:37 +0800] "GET /static/js/interactions.js HTTP/1.1" 404 207 "http://localhost:5002/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 5231 +127.0.0.1 - - [11/Oct/2025:00:06:38 +0800] "GET /api/check-login HTTP/1.1" 200 35 "http://localhost:5002/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 1095 +127.0.0.1 - - [11/Oct/2025:00:06:58 +0800] "GET /login HTTP/1.1" 200 23192 "http://localhost:5002/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 473382 +127.0.0.1 - - [11/Oct/2025:00:06:58 +0800] "GET /static/js/interactions.js HTTP/1.1" 404 207 "http://localhost:5002/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 5083 +127.0.0.1 - - [11/Oct/2025:00:06:58 +0800] "GET /api/check-login HTTP/1.1" 200 35 "http://localhost:5002/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 1003 +127.0.0.1 - - [11/Oct/2025:00:07:02 +0800] "GET /login HTTP/1.1" 200 23192 "http://localhost:5002/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 437084 +127.0.0.1 - - [11/Oct/2025:00:07:02 +0800] "GET /static/js/interactions.js HTTP/1.1" 404 207 "http://localhost:5002/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 1292 +127.0.0.1 - - [11/Oct/2025:00:07:02 +0800] "GET /api/check-login HTTP/1.1" 200 35 "http://localhost:5002/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 1117 +127.0.0.1 - - [11/Oct/2025:00:07:11 +0800] "POST /api/login HTTP/1.1" 200 79 "http://localhost:5002/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 107053 +127.0.0.1 - - [11/Oct/2025:00:07:15 +0800] "POST /api/login HTTP/1.1" 200 174 "http://localhost:5002/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 238946 +127.0.0.1 - - [11/Oct/2025:00:07:17 +0800] "GET / HTTP/1.1" 200 1404048 "http://localhost:5002/login" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 223912 +127.0.0.1 - - [11/Oct/2025:00:07:19 +0800] "GET /static/js/interactions.js HTTP/1.1" 404 207 "http://localhost:5002/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 1076 +127.0.0.1 - - [11/Oct/2025:00:07:20 +0800] "GET /api/check-login HTTP/1.1" 200 115 "http://localhost:5002/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" 969 diff --git a/logs/gunicorn_error.log b/logs/gunicorn_error.log index b602bac..71e07ba 100644 --- a/logs/gunicorn_error.log +++ b/logs/gunicorn_error.log @@ -6278,3 +6278,34 @@ sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between pa [2025-10-11 00:01:47 +0800] [1927] [INFO] Booting worker with pid: 1927 [2025-10-11 00:01:47 +0800] [1927] [INFO] 工作进程 1927 已启动 [2025-10-11 00:01:47 +0800] [1927] [INFO] 工作进程 1927 初始化完成 +[2025-10-11 00:06:01 +0800] [1906] [INFO] Handling signal: term +[2025-10-11 00:06:01 +0800] [1921] [INFO] Worker exiting (pid: 1921) +[2025-10-11 00:06:01 +0800] [1919] [INFO] Worker exiting (pid: 1919) +[2025-10-11 00:06:01 +0800] [1922] [INFO] Worker exiting (pid: 1922) +[2025-10-11 00:06:01 +0800] [1920] [INFO] Worker exiting (pid: 1920) +[2025-10-11 00:06:01 +0800] [1927] [INFO] Worker exiting (pid: 1927) +[2025-10-11 00:06:02 +0800] [1906] [INFO] Shutting down: Master +[2025-10-11 00:06:23 +0800] [28527] [INFO] Starting gunicorn 21.2.0 +[2025-10-11 00:06:23 +0800] [28527] [INFO] Gunicorn服务器启动中... +[2025-10-11 00:06:23 +0800] [28527] [INFO] Listening at: http://0.0.0.0:5002 (28527) +[2025-10-11 00:06:23 +0800] [28527] [INFO] Using worker: sync +[2025-10-11 00:06:23 +0800] [28527] [INFO] 工作进程 [booting] 即将启动 +[2025-10-11 00:06:23 +0800] [28537] [INFO] Booting worker with pid: 28537 +[2025-10-11 00:06:23 +0800] [28537] [INFO] 工作进程 28537 已启动 +[2025-10-11 00:06:23 +0800] [28537] [INFO] 工作进程 28537 初始化完成 +[2025-10-11 00:06:24 +0800] [28527] [INFO] 工作进程 [booting] 即将启动 +[2025-10-11 00:06:24 +0800] [28538] [INFO] Booting worker with pid: 28538 +[2025-10-11 00:06:24 +0800] [28538] [INFO] 工作进程 28538 已启动 +[2025-10-11 00:06:24 +0800] [28538] [INFO] 工作进程 28538 初始化完成 +[2025-10-11 00:06:24 +0800] [28527] [INFO] 工作进程 [booting] 即将启动 +[2025-10-11 00:06:24 +0800] [28539] [INFO] Booting worker with pid: 28539 +[2025-10-11 00:06:24 +0800] [28539] [INFO] 工作进程 28539 已启动 +[2025-10-11 00:06:24 +0800] [28539] [INFO] 工作进程 28539 初始化完成 +[2025-10-11 00:06:24 +0800] [28527] [INFO] 工作进程 [booting] 即将启动 +[2025-10-11 00:06:24 +0800] [28540] [INFO] Booting worker with pid: 28540 +[2025-10-11 00:06:24 +0800] [28540] [INFO] 工作进程 28540 已启动 +[2025-10-11 00:06:24 +0800] [28540] [INFO] 工作进程 28540 初始化完成 +[2025-10-11 00:06:24 +0800] [28527] [INFO] 工作进程 [booting] 即将启动 +[2025-10-11 00:06:24 +0800] [28542] [INFO] Booting worker with pid: 28542 +[2025-10-11 00:06:24 +0800] [28542] [INFO] 工作进程 28542 已启动 +[2025-10-11 00:06:24 +0800] [28542] [INFO] 工作进程 28542 初始化完成 diff --git a/src/flask_prompt_master/static/css/style.css b/src/flask_prompt_master/static/css/style.css index b82a38f..98ed16a 100644 --- a/src/flask_prompt_master/static/css/style.css +++ b/src/flask_prompt_master/static/css/style.css @@ -154,7 +154,7 @@ nav a:hover { } .feature:hover { - transform: translateY(-4px); + transform: translateY(-4px) scale(1.02); box-shadow: var(--shadow-xl); border-color: var(--primary-light); } @@ -163,6 +163,50 @@ nav a:hover { transform: scaleX(1); } +/* 模板卡片增强效果 */ +.template-card { + position: relative; + overflow: hidden; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); +} + +.template-card::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(99, 102, 241, 0.1), transparent); + transition: left 0.6s ease; +} + +.template-card:hover::before { + left: 100%; +} + +.template-card:hover { + transform: translateY(-6px) scale(1.03); + box-shadow: 0 20px 40px rgba(99, 102, 241, 0.15); +} + +/* 卡片内容动画 */ +.template-card h3 { + transition: color 0.3s ease; +} + +.template-card:hover h3 { + color: var(--primary-color); +} + +.template-card p { + transition: transform 0.3s ease; +} + +.template-card:hover p { + transform: translateY(-2px); +} + .feature h2 { color: #2c3e50; margin-bottom: 15px; @@ -221,9 +265,23 @@ nav a:hover { .form-control:focus { outline: none; - border-color: var(--primary-color); - box-shadow: 0 0 0 3px rgba(30, 58, 138, 0.1); - transform: translateY(-1px); + border: 2px solid transparent; + background: linear-gradient(white, white) padding-box, + var(--gradient-primary) border-box; + box-shadow: 0 0 0 3px rgba(30, 58, 138, 0.1), + 0 4px 12px rgba(99, 102, 241, 0.15); + transform: translateY(-2px); + animation: focusGlow 0.3s ease-out; +} + +@keyframes focusGlow { + 0% { + box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.4); + } + 100% { + box-shadow: 0 0 0 3px rgba(30, 58, 138, 0.1), + 0 4px 12px rgba(99, 102, 241, 0.15); + } } .form-control::placeholder { @@ -274,6 +332,55 @@ nav a:hover { box-shadow: var(--shadow-lg); } +.btn-primary:active { + transform: translateY(0); + box-shadow: var(--shadow-md); + animation: buttonPulse 0.3s ease-out; +} + +@keyframes buttonPulse { + 0% { + box-shadow: var(--shadow-md); + } + 50% { + box-shadow: 0 0 20px rgba(99, 102, 241, 0.4), + var(--shadow-lg); + } + 100% { + box-shadow: var(--shadow-md); + } +} + +/* 生成按钮特殊效果 */ +.btn-generate { + position: relative; + overflow: hidden; + background: var(--gradient-primary); + border: none; + color: white; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.btn-generate::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + background: rgba(255, 255, 255, 0.3); + border-radius: 50%; + transform: translate(-50%, -50%); + transition: width 0.6s, height 0.6s; +} + +.btn-generate:active::after { + width: 300px; + height: 300px; +} + .btn-secondary { background: var(--neutral-100); color: var(--neutral-700); @@ -351,6 +458,151 @@ nav a:hover { margin-top: var(--spacing-2); } +/* 骨架屏加载动画 */ +.skeleton { + background: linear-gradient(90deg, var(--neutral-200) 25%, var(--neutral-100) 50%, var(--neutral-200) 75%); + background-size: 200% 100%; + animation: skeletonLoading 1.5s infinite; + border-radius: var(--radius-md); +} + +@keyframes skeletonLoading { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} + +.skeleton-text { + height: 1rem; + margin-bottom: var(--spacing-2); +} + +.skeleton-text:last-child { + width: 60%; +} + +.skeleton-card { + background: white; + padding: var(--spacing-6); + border-radius: var(--radius-xl); + box-shadow: var(--shadow-md); + margin-bottom: var(--spacing-4); +} + +.skeleton-button { + height: 2.5rem; + width: 8rem; + border-radius: var(--radius-md); +} + +/* 加载状态 */ +.loading-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(255, 255, 255, 0.9); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; +} + +.loading-spinner { + width: 3rem; + height: 3rem; + border: 4px solid var(--neutral-200); + border-top: 4px solid var(--primary-color); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* 打字机效果 */ +.typewriter { + overflow: hidden; + border-right: 2px solid var(--primary-color); + white-space: nowrap; + animation: typing 3s steps(40, end), blink-caret 0.75s step-end infinite; +} + +@keyframes typing { + from { width: 0; } + to { width: 100%; } +} + +@keyframes blink-caret { + from, to { border-color: transparent; } + 50% { border-color: var(--primary-color); } +} + +/* 结果展示动画 */ +.result-fade-in { + animation: resultFadeIn 0.8s ease-out; +} + +@keyframes resultFadeIn { + 0% { + opacity: 0; + transform: translateY(20px); + } + 100% { + opacity: 1; + transform: translateY(0); + } +} + +/* 粒子效果 */ +.particle-container { + position: relative; + overflow: hidden; +} + +.particle { + position: absolute; + width: 4px; + height: 4px; + background: var(--primary-color); + border-radius: 50%; + animation: particleFloat 2s ease-out forwards; +} + +@keyframes particleFloat { + 0% { + opacity: 1; + transform: translateY(0) scale(1); + } + 100% { + opacity: 0; + transform: translateY(-100px) scale(0); + } +} + +/* 脉冲效果 */ +.pulse { + animation: pulse 2s infinite; +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.7); + } + 70% { + box-shadow: 0 0 0 10px rgba(99, 102, 241, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); + } +} + /* 响应式设计 */ @media (max-width: 768px) { .container { diff --git a/src/flask_prompt_master/static/js/interactions.js b/src/flask_prompt_master/static/js/interactions.js new file mode 100644 index 0000000..210878d --- /dev/null +++ b/src/flask_prompt_master/static/js/interactions.js @@ -0,0 +1,346 @@ +/** + * 交互增强脚本 + * 实现微交互动画、加载状态、粒子效果等 + */ + +// 等待DOM加载完成 +document.addEventListener('DOMContentLoaded', function() { + initializeInteractions(); +}); + +/** + * 初始化所有交互功能 + */ +function initializeInteractions() { + // 输入框聚焦效果 + initializeInputFocusEffects(); + + // 按钮动画效果 + initializeButtonAnimations(); + + // 卡片悬停效果 + initializeCardHoverEffects(); + + // 加载状态管理 + initializeLoadingStates(); + + // 粒子效果 + initializeParticleEffects(); + + // 打字机效果 + initializeTypewriterEffect(); + + console.log('🎨 交互增强功能已初始化'); +} + +/** + * 输入框聚焦效果 + */ +function initializeInputFocusEffects() { + const inputs = document.querySelectorAll('.form-control, input[type="text"], textarea'); + + inputs.forEach(input => { + // 添加聚焦类名 + input.addEventListener('focus', function() { + this.classList.add('focused'); + }); + + input.addEventListener('blur', function() { + this.classList.remove('focused'); + }); + + // 输入时的微动画 + input.addEventListener('input', function() { + if (this.value.length > 0) { + this.classList.add('has-content'); + } else { + this.classList.remove('has-content'); + } + }); + }); +} + +/** + * 按钮动画效果 + */ +function initializeButtonAnimations() { + const buttons = document.querySelectorAll('.btn, .btn-primary, .btn-generate'); + + buttons.forEach(button => { + // 点击波纹效果 + button.addEventListener('click', function(e) { + createRippleEffect(e, this); + }); + + // 悬停效果增强 + button.addEventListener('mouseenter', function() { + this.style.transform = 'translateY(-2px) scale(1.05)'; + }); + + button.addEventListener('mouseleave', function() { + this.style.transform = 'translateY(0) scale(1)'; + }); + }); +} + +/** + * 创建波纹效果 + */ +function createRippleEffect(event, element) { + const ripple = document.createElement('span'); + const rect = element.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height); + const x = event.clientX - rect.left - size / 2; + const y = event.clientY - rect.top - size / 2; + + ripple.style.width = ripple.style.height = size + 'px'; + ripple.style.left = x + 'px'; + ripple.style.top = y + 'px'; + ripple.classList.add('ripple'); + + element.appendChild(ripple); + + setTimeout(() => { + ripple.remove(); + }, 600); +} + +/** + * 卡片悬停效果 + */ +function initializeCardHoverEffects() { + const cards = document.querySelectorAll('.feature, .template-card, .prompt-card'); + + cards.forEach(card => { + card.addEventListener('mouseenter', function() { + this.style.transform = 'translateY(-6px) scale(1.03)'; + this.style.boxShadow = '0 20px 40px rgba(99, 102, 241, 0.15)'; + }); + + card.addEventListener('mouseleave', function() { + this.style.transform = 'translateY(0) scale(1)'; + this.style.boxShadow = ''; + }); + }); +} + +/** + * 加载状态管理 + */ +function initializeLoadingStates() { + // 显示加载状态 + window.showLoading = function(message = '正在生成中...') { + const overlay = document.createElement('div'); + overlay.className = 'loading-overlay'; + overlay.innerHTML = ` +
${message}
+