Compare commits

...

4 Commits

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

@ -0,0 +1,2 @@
# 复制此文件为.env并填入你的API密钥
DEEPSEEK_API_KEY=sk-2685bfd0fe054c9f82a12ac7905dd80d

@ -0,0 +1,2 @@
# 复制此文件为.env并填入你的API密钥
DEEPSEEK_API_KEY=sk-2685bfd0fe054c9f82a12ac7905dd80d

@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
<excludeFolder url="file://$MODULE_DIR$/.venv1" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="USE_PROJECT_PROFILE" value="false" />
<version value="1.0" />
</settings>
</component>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.13 (PythonProject2)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13" project-jdk-type="Python SDK" />
</project>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/PythonProject2.iml" filepath="$PROJECT_DIR$/.idea/PythonProject2.iml" />
</modules>
</component>
</project>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
</project>

@ -0,0 +1,17 @@
#OpenAI SDK
# Please install OpenAI SDK first: `pip3 install openai`
from openai import OpenAI
client = OpenAI(api_key="sk-2685bfd0fe054c9f82a12ac7905dd80d", base_url="https://api.deepseek.com")
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": "你是一个情绪分析大师,请用委婉的语气说话"},
{"role": "user", "content": "输入日记内容"},
],
stream=False
)
print(response.choices[0].message.content)

@ -0,0 +1,24 @@
import sqlite3
# 连接到数据库
try:
conn = sqlite3.connect('database.db')
c = conn.cursor()
# 查看所有表
print("数据库中的表:")
c.execute("SELECT name FROM sqlite_master WHERE type='table';")
tables = c.fetchall()
for table in tables:
print(f"- {table[0]}")
# 查看表结构
print(f" {table[0]}表结构:")
c.execute(f"PRAGMA table_info({table[0]});")
columns = c.fetchall()
for column in columns:
print(f" - {column[1]} ({column[2]})")
conn.close()
except Exception as e:
print(f"检查数据库出错: {e}")

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

@ -0,0 +1,5 @@
flask==2.3.3
flask-cors==4.0.0
requests==2.31.0
python-dotenv==1.0.0
openai==1.3.7

@ -0,0 +1,601 @@
:root {
/* 主题色彩方案 */
--primary-green: #e8f5e9; /* 主淡绿色 */
--secondary-green: #c8e6c9; /* 次淡绿色 */
--accent-green: #a5d6a7; /* 强调淡绿色 */
--light-green: #f1f8e9; /* 浅色淡绿色 */
--dark-green: #81c784; /* 深色淡绿色 */
--text-color: #4a4a4a; /* 文本颜色 */
--heading-color: #333333; /* 标题颜色 */
--border-radius: 16px; /* 圆角增强 */
--box-shadow: 0 6px 24px rgba(0,0,0,0.08); /* 阴影增强 */
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); /* 过渡动画 */
/* 情绪标签颜色 */
--color-orange-100: #ffe0cc;
--color-orange-800: #cc6633;
--color-pink-100: #ffcce0;
--color-pink-800: #cc3366;
--color-indigo-100: #ccd6ff;
--color-indigo-800: #3366cc;
--color-cyan-100: #ccf2ff;
--color-cyan-800: #33ccff;
--color-gray-100: #f0f0f0;
--color-gray-800: #666666;
/* 活动标签颜色 */
--color-green-200: #e0ffcc;
--color-green-800: #66cc33;
--color-blue-200: #cce0ff;
--color-blue-800: #3366cc;
--color-red-200: #ffcce0;
--color-red-800: #cc3366;
--color-purple-200: #e6ccff;
--color-purple-800: #9933cc;
--color-yellow-200: #fff2cc;
--color-yellow-800: #cc9933;
--color-orange-200: #ffecd9;
--color-orange-800: #ff9933;
--color-pink-200: #ffd9ec;
--color-pink-800: #ff3399;
/* 场景标签颜色 */
--color-gray-200: #f5f5f5;
--color-yellow-300: #fff5cc;
--color-orange-300: #ffe6cc;
--color-blue-300: #cce6ff;
--color-green-300: #d9ffcc;
--color-purple-300: #e6d9ff;
}
/* 全局样式重置 */
body {
background-image: url('/mypo/greenback.jpg');
background-size: cover;
background-position: center bottom;
background-repeat: no-repeat;
background-attachment: fixed;
color: var(--text-color);
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
line-height: 1.7;
min-height: 100vh;
margin: 0;
padding: 0;
overflow-x: hidden; /* 防止水平滚动条 */
}
html {
min-height: 100%;
overflow-x: hidden; /* 防止水平滚动条 */
}
/* 标签样式 */
.tag-item {
cursor: pointer;
transition: var(--transition);
font-weight: 500;
}
.tag-item:hover {
transform: scale(1.05);
box-shadow: var(--box-shadow);
}
/* 情绪标签颜色样式 */
.bg-orange-100 { background-color: var(--color-orange-100); }
.text-orange-800 { color: var(--color-orange-800); }
.bg-pink-100 { background-color: var(--color-pink-100); }
.text-pink-800 { color: var(--color-pink-800); }
.bg-indigo-100 { background-color: var(--color-indigo-100); }
.text-indigo-800 { color: var(--color-indigo-800); }
.bg-cyan-100 { background-color: var(--color-cyan-100); }
.text-cyan-800 { color: var(--color-cyan-800); }
.bg-gray-100 { background-color: var(--color-gray-100); }
.text-gray-800 { color: var(--color-gray-800); }
/* 活动标签颜色样式 */
.bg-green-200 { background-color: var(--color-green-200); }
.text-green-800 { color: var(--color-green-800); }
.bg-blue-200 { background-color: var(--color-blue-200); }
.text-blue-800 { color: var(--color-blue-800); }
.bg-red-200 { background-color: var(--color-red-200); }
.text-red-800 { color: var(--color-red-800); }
.bg-purple-200 { background-color: var(--color-purple-200); }
.text-purple-800 { color: var(--color-purple-800); }
.bg-yellow-200 { background-color: var(--color-yellow-200); }
.text-yellow-800 { color: var(--color-yellow-800); }
.bg-orange-200 { background-color: var(--color-orange-200); }
.text-orange-800 { color: var(--color-orange-800); }
.bg-pink-200 { background-color: var(--color-pink-200); }
.text-pink-800 { color: var(--color-pink-800); }
/* 场景标签颜色样式 */
.bg-gray-200 { background-color: var(--color-gray-200); }
.bg-yellow-300 { background-color: var(--color-yellow-300); }
.bg-orange-300 { background-color: var(--color-orange-300); }
.bg-blue-300 { background-color: var(--color-blue-300); }
.bg-green-300 { background-color: var(--color-green-300); }
.bg-purple-300 { background-color: var(--color-purple-300); }
/* 导航栏样式 */
.navbar {
background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%) !important;
box-shadow: var(--box-shadow);
padding: 0.8rem 0;
}
.navbar-brand {
color: var(--heading-color) !important;
font-weight: 800;
font-size: 1.6rem;
text-shadow: 0 2px 4px rgba(255,255,255,0.3);
transition: var(--transition);
}
.navbar-brand:hover {
transform: scale(1.05);
}
.nav-link {
color: var(--heading-color) !important;
font-weight: 600;
position: relative;
padding: 0.8rem 1rem !important;
margin: 0 0.3rem;
border-radius: var(--border-radius);
transition: var(--transition);
}
.nav-link:hover {
background-color: rgba(255,255,255,0.2);
transform: translateY(-2px);
}
.nav-link::after {
content: '';
position: absolute;
width: 0;
height: 3px;
bottom: 0.5rem;
left: 50%;
background-color: var(--heading-color);
transition: var(--transition);
border-radius: 3px;
}
.nav-link:hover::after {
width: 70%;
left: 15%;
}
/* 下拉菜单样式 */
.dropdown-menu {
background-color: white !important;
border: 2px solid var(--primary-green) !important;
border-radius: var(--border-radius) !important;
box-shadow: var(--box-shadow) !important;
margin-top: 0.5rem !important;
overflow: hidden;
}
.dropdown-item {
color: var(--text-color) !important;
font-weight: 500;
padding: 0.8rem 1.5rem !important;
transition: var(--transition);
border-radius: 0 !important;
}
.dropdown-item:hover {
background-color: var(--light-green) !important;
color: var(--heading-color) !important;
transform: translateX(5px);
}
.dropdown-item.active {
background-color: var(--primary-green) !important;
color: var(--heading-color) !important;
}
/* 英雄区域样式 */
.hero-section {
background-color: rgba(255, 255, 255, 0.02); /* 设置透明度为0.02(保持整个页面蒙版透明度一致) */
color: var(--heading-color);
min-height: 100vh;
display: flex;
align-items: center;
position: relative;
overflow: hidden;
backdrop-filter: blur(5px);
}
.hero-text {
min-width: 0;
}
.hero-text h1 {
text-align: left;
white-space: normal;
display: block;
}
/* 卡片样式 */
.card {
background-color: white;
border: 2px solid var(--primary-green);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
transition: var(--transition);
overflow: hidden;
}
.card:hover {
transform: translateY(-10px);
box-shadow: 0 15px 40px rgba(0,0,0,0.12);
border-color: var(--secondary-green);
}
.card-header {
background: linear-gradient(135deg, var(--primary-green) 0%, var(--light-green) 100%);
color: var(--heading-color);
font-weight: 700;
padding: 1.2rem 1.5rem;
border-bottom: 2px solid var(--primary-green);
}
.card-title {
color: var(--heading-color);
font-weight: 700;
margin-bottom: 0.8rem;
}
/* 按钮样式 */
.btn {
border-radius: var(--border-radius);
font-weight: 600;
padding: 0.8rem 2rem;
transition: var(--transition);
border: 2px solid transparent;
position: relative;
overflow: hidden;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.btn-primary {
background-color: var(--primary-green);
color: var(--heading-color);
border-color: var(--primary-green);
}
.btn-primary:hover {
background-color: var(--secondary-green);
border-color: var(--secondary-green);
color: var(--heading-color);
transform: translateY(-2px);
box-shadow: 0 10px 30px rgba(165, 214, 167, 0.3);
}
.btn-secondary {
background-color: white;
color: var(--heading-color);
border-color: var(--primary-green);
}
.btn-secondary:hover {
background-color: var(--light-green);
border-color: var(--secondary-green);
transform: translateY(-2px);
}
.btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.4), transparent);
transition: var(--transition);
}
.btn:hover::before {
left: 100%;
}
/* 表单样式 */
.form-control {
border-radius: var(--border-radius);
border: 2px solid #e9ecef;
padding: 1rem 1.2rem;
transition: var(--transition);
background-color: white;
}
.form-control:focus {
border-color: var(--primary-green);
box-shadow: 0 0 0 0.3rem rgba(165, 214, 167, 0.25);
transform: translateY(-1px);
}
.form-label {
color: var(--heading-color);
font-weight: 600;
margin-bottom: 0.5rem;
}
/* 标题样式 */
h1, h2, h3, h4, h5, h6 {
color: var(--heading-color);
font-weight: 700;
line-height: 1.3;
}
h1 {
font-size: 3rem;
margin-bottom: 1.5rem;
}
h2 {
font-size: 2.2rem;
margin-bottom: 1.2rem;
}
h3 {
font-size: 1.8rem;
margin-bottom: 1rem;
}
/* 段落样式 */
p {
color: var(--text-color);
font-size: 1.1rem;
margin-bottom: 1.2rem;
line-height: 1.8;
}
/* 警告框样式 */
.alert {
border-radius: var(--border-radius);
padding: 1.2rem 1.5rem;
margin-bottom: 1.5rem;
box-shadow: var(--box-shadow);
border: 2px solid transparent;
}
.alert-success {
background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
color: #155724;
border-left: 5px solid #28a745;
}
.alert-danger {
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
color: #721c24;
border-left: 5px solid #dc3545;
}
.alert-info {
background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%);
color: #0c5460;
border-left: 5px solid #17a2b8;
}
/* 页脚样式 */
.footer {
background: linear-gradient(135deg, var(--primary-green) 0%, var(--dark-green) 100%);
color: var(--heading-color);
padding: 4rem 0 2rem;
margin-top: 4rem;
position: relative;
}
.footer a {
color: var(--heading-color);
text-decoration: none;
transition: var(--transition);
}
.footer a:hover {
color: white;
transform: translateX(3px);
}
/* 响应式设计 */
@media (max-width: 768px) {
.navbar {
padding: 0.5rem 0;
}
.nav-link {
padding: 0.6rem 0.8rem !important;
margin: 0.2rem 0;
}
.hero-section {
padding: 80px 0;
}
h1 {
font-size: 2.2rem;
}
h2 {
font-size: 1.8rem;
}
.btn {
padding: 0.7rem 1.5rem;
font-size: 0.9rem;
}
}
/* 加载动画 */
.loading {
display: inline-block;
width: 24px;
height: 24px;
border: 3px solid rgba(74, 74, 74, 0.2);
border-radius: 50%;
border-top-color: var(--heading-color);
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 淡入动画 */
.fade-in {
animation: fadeIn 0.6s ease-in-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 卡片增强效果 */
.card-enhanced {
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
position: relative;
overflow: hidden;
}
.card-enhanced::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
transition: left 0.5s;
}
.card-enhanced:hover::before {
left: 100%;
}
.card-enhanced:hover {
transform: translateY(-12px) scale(1.03);
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
}
.feature-card {
padding: 1.5rem;
border-radius: var(--border-radius);
}
/* 徽章样式 */
.badge {
border-radius: 20px;
padding: 0.5rem 1rem;
font-weight: 600;
font-size: 0.85rem;
}
.badge-primary {
background-color: var(--primary-green);
color: var(--heading-color);
}
/* 列表样式 */
.list-group-item {
border: none;
border-radius: var(--border-radius);
margin-bottom: 0.8rem;
transition: var(--transition);
padding: 1rem 1.2rem;
background-color: white;
border: 1px solid #e9ecef;
}
.list-group-item:hover {
background-color: var(--light-green);
transform: translateX(8px);
border-color: var(--primary-green);
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--light-green);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: var(--primary-green);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--secondary-green);
}
/* 按钮点击反馈效果 */
.btn-feedback {
position: relative;
overflow: hidden;
}
.btn-feedback::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(255, 255, 255, 0.4);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.btn-feedback.clicked::after {
width: 400px;
height: 400px;
}
/* 图表样式如果使用ECharts */
.chart-container {
background-color: white;
border-radius: var(--border-radius);
padding: 1.5rem;
box-shadow: var(--box-shadow);
border: 2px solid var(--primary-green);
}
/* AI聊天窗口样式 */
.ai-chat-window {
background-color: white;
border-radius: var(--border-radius);
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
border: 2px solid var(--primary-green);
}
.chat-header {
background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%);
color: var(--heading-color);
}
.chat-body {
background: linear-gradient(to bottom, var(--light-green), var(--primary-green));
}
.user-message .message-content {
background: linear-gradient(135deg, var(--primary-green) 0%, var(--secondary-green) 100%);
color: var(--heading-color);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

@ -0,0 +1,802 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}AI日记{% endblock %}</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Font Awesome -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<!-- ECharts -->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
<!-- Marked.js for Markdown -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- 主题CSS -->
<link rel="stylesheet" href="{{ url_for('static', filename='css/theme.css') }}">
<style>
:root {
--primary-color: #e8f5e9;
--secondary-color: #c8e6c9;
--accent-color: #a5d6a7;
--success-color: #28a745;
--warning-color: #ffc107;
--danger-color: #dc3545;
--light-bg: #f8f9fa;
--dark-text: #333;
--border-radius: 12px;
--box-shadow: 0 4px 20px rgba(0,0,0,0.1);
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--dark-text);
}
.navbar {
background: rgba(165, 214, 167, 0.9) !important; /* 更淡的草绿色,增加透明度 */
box-shadow: var(--box-shadow);
/* 移除backdrop-filter使用统一的固定蒙板 */
position: relative;
z-index: 10000 !important;
}
.navbar .dropdown-menu {
z-index: 10001 !important;
position: absolute !important;
}
.navbar .nav-item.dropdown {
position: relative;
z-index: 10001 !important;
}
.dropdown-menu.show {
z-index: 10002 !important;
position: absolute !important;
}
.navbar-brand {
font-weight: 700;
font-size: 1.5rem;
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
.nav-link {
font-weight: 500;
transition: var(--transition);
position: relative;
}
.nav-link:hover {
transform: translateY(-2px);
}
.nav-link::after {
content: '';
position: absolute;
width: 0;
height: 2px;
bottom: 0;
left: 50%;
background-color: white;
transition: var(--transition);
}
.nav-link:hover::after {
width: 100%;
left: 0;
}
/* 固定的半透明蒙板层 */
.fixed-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw; /* 使用视口宽度确保完全覆盖 */
height: 100vh; /* 使用视口高度确保完全覆盖 */
background-color: rgba(255, 255, 255, 0.02);
backdrop-filter: blur(5px);
z-index: -1;
pointer-events: none;
overflow: hidden; /* 防止任何溢出内容 */
}
.hero-section {
color: var(--dark-text);
min-height: 100vh;
display: flex;
align-items: center;
position: relative;
overflow: hidden;
z-index: 1 !important;
}
/* 调整内容位置:右移并上移 */
.hero-section .container {
padding-left: 50px; /* 右移一段距离,与小飞机标志对齐 */
margin: -190px 0 60px 0; /* 再上移80px总共上移约250px保持底部空间 */
}
.hero-section .container {
position: relative;
z-index: 2;
}
.container {
position: relative;
z-index: 1;
}
.btn {
border-radius: var(--border-radius);
font-weight: 600;
padding: 12px 30px;
transition: var(--transition);
border: none;
position: relative;
overflow: hidden;
}
.btn::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: var(--transition);
}
.btn:hover::before {
left: 100%;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0,0,0,0.2);
}
.card {
border: none;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
transition: var(--transition);
overflow: hidden;
}
.card:hover {
transform: translateY(-8px);
box-shadow: 0 12px 40px rgba(0,0,0,0.15);
}
.product-card {
transition: var(--transition);
border: none;
box-shadow: var(--box-shadow);
border-radius: var(--border-radius);
}
.product-card:hover {
transform: translateY(-8px) scale(1.02);
box-shadow: 0 15px 50px rgba(0,0,0,0.2);
}
.card-img-top {
transition: var(--transition);
}
.card:hover .card-img-top {
transform: scale(1.05);
}
.ai-chat-float {
position: fixed;
bottom: 30px;
right: 30px;
z-index: 1000;
}
.ai-chat-btn {
width: 70px;
height: 70px;
border-radius: 50%;
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
border: 3px solid var(--primary-color);
color: white;
font-size: 0;
box-shadow: 0 8px 25px rgba(30, 60, 114, 0.4);
transition: var(--transition);
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
padding: 5px;
}
.ai-chat-btn img {
width: 100%;
height: 100%;
object-fit: contain;
border-radius: 50%;
transition: var(--transition);
background: white;
}
.ai-chat-btn:hover {
transform: scale(1.1) rotate(5deg);
box-shadow: 0 12px 35px rgba(30, 60, 114, 0.6);
border-color: var(--accent-color);
}
.ai-chat-btn:hover img {
transform: scale(1.05);
}
.ai-chat-btn::before {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(74, 144, 226, 0.2);
border-radius: 50%;
transition: var(--transition);
transform: translate(-50%, -50%);
z-index: 1;
}
.ai-chat-btn:hover::before {
width: 120%;
height: 120%;
}
.ai-chat-btn img {
position: relative;
z-index: 2;
}
.ai-chat-window {
position: fixed;
bottom: 120px;
right: 30px;
width: 380px;
height: 550px;
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
display: none;
z-index: 1001;
overflow: hidden;
border: 1px solid rgba(255,255,255,0.2);
}
.chat-header {
background: linear-gradient(135deg, var(--accent-color) 0%, var(--primary-color) 100%);
color: var(--dark-text);
padding: 20px;
font-weight: 600;
position: relative;
}
.chat-header::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
}
.chat-body {
height: 380px;
overflow-y: auto;
padding: 20px;
background: linear-gradient(to bottom, #f8f9fa, #ffffff);
}
.chat-body::-webkit-scrollbar {
width: 6px;
}
.chat-body::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 3px;
}
.chat-body::-webkit-scrollbar-thumb {
background: var(--accent-color);
border-radius: 3px;
}
.chat-input {
padding: 20px;
border-top: 1px solid #eee;
background: white;
}
.message {
margin-bottom: 20px;
animation: fadeInUp 0.3s ease;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.user-message {
text-align: right;
}
.user-message .message-content {
background: linear-gradient(135deg, var(--accent-color) 0%, var(--primary-color) 100%);
color: var(--dark-text);
padding: 12px 18px;
border-radius: 20px 20px 8px 20px;
display: inline-block;
max-width: 80%;
box-shadow: 0 4px 15px rgba(165, 214, 167, 0.3);
}
.ai-message .message-content {
background: white;
color: var(--dark-text);
padding: 12px 18px;
border-radius: 20px 20px 20px 8px;
display: inline-block;
max-width: 80%;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
border: 1px solid #e9ecef;
}
.footer {
background: linear-gradient(135deg, var(--primary-color) 0%, var(--accent-color) 100%);
color: var(--dark-text);
padding: 60px 0 40px;
margin-top: 80px;
position: relative;
}
.footer::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
}
.alert {
border: none;
border-radius: var(--border-radius);
padding: 16px 20px;
margin-bottom: 20px;
box-shadow: var(--box-shadow);
animation: slideInDown 0.3s ease;
}
@keyframes slideInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.alert-success {
background: linear-gradient(135deg, #d4edda 0%, #c3e6cb 100%);
color: #155724;
border-left: 4px solid var(--success-color);
}
.alert-danger {
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%);
color: #721c24;
border-left: 4px solid var(--danger-color);
}
.alert-info {
background: linear-gradient(135deg, #d1ecf1 0%, #bee5eb 100%);
color: #0c5460;
border-left: 4px solid #17a2b8;
}
.form-control {
border-radius: var(--border-radius);
border: 2px solid #e9ecef;
padding: 12px 16px;
transition: var(--transition);
}
.form-control:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 0.2rem rgba(74, 144, 226, 0.25);
}
.input-group-text {
border-radius: var(--border-radius) 0 0 var(--border-radius);
border: 2px solid #e9ecef;
border-right: none;
background: var(--light-bg);
}
.badge {
border-radius: 20px;
padding: 6px 12px;
font-weight: 500;
}
.list-group-item {
border: none;
border-radius: var(--border-radius);
margin-bottom: 8px;
transition: var(--transition);
}
.list-group-item:hover {
background: var(--light-bg);
transform: translateX(5px);
}
.dropdown-menu {
border: none !important;
border-radius: var(--border-radius) !important;
box-shadow: var(--box-shadow) !important;
padding: 10px !important;
z-index: 10001 !important;
position: absolute !important;
background: white !important;
}
.dropdown-item {
border-radius: 8px;
transition: var(--transition);
}
.dropdown-item:hover {
background: var(--light-bg);
transform: translateX(5px);
}
/* 响应式设计 */
@media (max-width: 768px) {
.ai-chat-window {
width: calc(100vw - 40px);
right: 20px;
left: 20px;
}
.hero-section {
padding: 80px 0;
}
.ai-chat-btn {
width: 60px;
height: 60px;
bottom: 20px;
right: 20px;
}
.ai-chat-btn img {
width: 35px;
height: 35px;
}
}
/* 加载动画 */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid rgba(255,255,255,.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 思考中动画 */
.thinking-indicator {
display: flex;
align-items: center;
padding: 12px 18px;
background: white;
border-radius: 20px 20px 20px 8px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
border: 1px solid #e9ecef;
margin-bottom: 20px;
animation: fadeInUp 0.3s ease;
}
.thinking-dots {
display: flex;
gap: 4px;
margin-left: 8px;
}
.thinking-dot {
width: 8px;
height: 8px;
background: var(--accent-color);
border-radius: 50%;
animation: thinking 1.4s infinite ease-in-out;
}
.thinking-dot:nth-child(1) { animation-delay: -0.32s; }
.thinking-dot:nth-child(2) { animation-delay: -0.16s; }
.thinking-dot:nth-child(3) { animation-delay: 0s; }
@keyframes thinking {
0%, 80%, 100% {
transform: scale(0.8);
opacity: 0.5;
}
40% {
transform: scale(1);
opacity: 1;
}
}
.typing-indicator {
color: var(--accent-color);
font-size: 14px;
font-weight: 500;
}
/* 确保下拉菜单显示在最顶层 */
.navbar-nav .dropdown-menu {
z-index: 10003 !important;
position: absolute !important;
top: 100% !important;
left: 0 !important;
display: none;
min-width: 200px;
background-color: white !important;
border: 1px solid rgba(0,0,0,.15) !important;
border-radius: var(--border-radius) !important;
box-shadow: 0 6px 12px rgba(0,0,0,.175) !important;
}
.navbar-nav .dropdown-menu.show {
display: block !important;
z-index: 10003 !important;
}
.navbar-nav .dropdown:hover .dropdown-menu {
display: block !important;
}
/* 按钮点击反馈效果 */
.btn-feedback {
position: relative;
overflow: hidden;
}
.btn-feedback::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-feedback.clicked::after {
width: 300px;
height: 300px;
}
/* 表单提交状态 */
.form-submitting {
opacity: 0.7;
pointer-events: none;
}
.form-submitting .btn {
position: relative;
}
.form-submitting .btn::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid transparent;
border-top: 2px solid currentColor;
border-radius: 50%;
animation: spin 1s linear infinite;
}
/* 成功提示动画 */
.success-animation {
animation: successPulse 0.6s ease-in-out;
}
@keyframes successPulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); background-color: var(--success-color); }
100% { transform: scale(1); }
}
/* 卡片悬停增强效果 */
.card-enhanced {
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
position: relative;
overflow: hidden;
}
.card-enhanced::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
transition: left 0.5s;
}
.card-enhanced:hover::before {
left: 100%;
}
.card-enhanced:hover {
transform: translateY(-10px) scale(1.02);
box-shadow: 0 20px 60px rgba(0,0,0,0.2);
}
/* 输入框焦点增强效果 */
.form-control:focus {
border-color: var(--accent-color);
box-shadow: 0 0 0 0.2rem rgba(74, 144, 226, 0.25);
transform: translateY(-2px);
}
.user-message .message-content::before {
content: '';
position: absolute;
top: 0;
right: 0;
width: 0;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2));
transition: width 0.3s;
}
.user-message:hover .message-content::before {
width: 100%;
}
</style>
{% block extra_css %}{% endblock %}
</head>
<body>
<div class="fixed-overlay"></div>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container">
<a class="navbar-brand" href="{{ url_for('index') }}">
<i class="fas fa-plane me-2"></i>AI日记
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="{{ url_for('index') }}">
<i class="fas fa-home me-1"></i>首页
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('diary_list') }}">
<i class="fas fa-book me-1"></i>我的日记
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('emotionanalyzer') }}">
<i class="fas fa-shopping-cart me-1"></i>AI周报
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('chuka') }}">
<i class="fas fa-book me-1"></i>我的卡册
</a>
</li>
</ul>
<ul class="navbar-nav">
{% if session.username %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown">
<i class="fas fa-user-circle me-1"></i>{{ session.username }}
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{{ url_for('profile') }}">
<i class="fas fa-user me-2"></i>个人中心
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="{{ url_for('logout') }}">
<i class="fas fa-sign-out-alt me-2"></i>退出登录
</a></li>
</ul>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{{ url_for('login') }}">
<i class="fas fa-sign-in-alt me-1"></i>登录
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{ url_for('register') }}">
<i class="fas fa-user-plus me-1"></i>注册
</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<!-- Flash Messages -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div class="container mt-3">
{% for category, message in messages %}
<div class="alert alert-{{ 'danger' if category == 'error' else category }} alert-dismissible fade show" role="alert">
<i class="fas fa-{{ 'check-circle' if category == 'success' else 'exclamation-triangle' if category == 'error' else 'info-circle' }} me-2"></i>
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<!-- Main Content -->
<main>
{% block content %}{% endblock %}
</main>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>

@ -0,0 +1,169 @@
{% extends "base.html" %}
{% block title %}我的卡册 - AI日记{% endblock %}
{% block extra_css %}
<style>
.containe {
max-width: 1000px;
margin: 0 auto;
}
header {
text-align: center;
margin-top: 40px;
margin-bottom: 30px;
}
h1 {
color: #333;
font-size: 2.2rem;
}
/* 抽卡按钮样式*/
#drawCardBtn {
background-color: #efec8e;
color: #fff;
border: none;
padding: 15px 30px;
border-radius: 10px;
cursor: pointer;
font-weight: bold;
font-size: 1.1rem;
box-shadow: 0 4px 12px rgba(255,215,0,0.3);
transition: all 0.3s ease;
}
#drawCardBtn:hover {
transform: scale(1.05);
box-shadow: 0 6px 16px rgba(255,215,0,0.4);
}
/* 分类卡片样式 */
.category-group {
margin-bottom: 20px;
}
/* 分类标题(点击折叠/展开) */
.category-header {
background: #fff;
padding: 25px 20px; /* 增加上下padding */
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
color: #333;
min-height: 80px;
}
/* 分类下的卡片列表 */
.category-cards {
display: none;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); /* 自适应列数 */
gap: 15px;
padding: 15px 20px;
background: #fff;
border-radius: 0 0 8px 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-top: -10px; /* 与标题无缝衔接 */
}
/* 单张卡片样式 */
.card-item {
text-align: center;
transition: transform 0.3s ease;
}
.card-item:hover {
transform: translateY(-5px); /* hover时轻微上浮 */
}
.card-item img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 8px;
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
}
.card-item p {
margin: 8px 0 0;
color: #666;
font-size: 0.95rem;
}
.card-item .time {
font-size: 0.85rem;
color: #999;
margin-top: 4px;
}
</style>
{% endblock %}
{% block content %}
<div class="containe">
<header>
<h1>我的卡册</h1>
<button id="drawCardBtn" onclick="window.location.href='/showcard'">今日抽卡</button>
</header>
<div id="categoryContainer">
<p style="text-align: center; color: #666;">加载中...</p>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// 页面加载时,按分类渲染用户已拥有的卡片
window.onload = async () => {
try {
const response = await fetch('/get_cards_by_category');
const categoryCards = await response.json(); // 格式:{分类名: [卡片列表]}
const container = document.getElementById('categoryContainer');
container.innerHTML = '';
// 无卡片时显示提示
if (Object.keys(categoryCards).length === 0) {
container.innerHTML = '<p style="text-align: center; color: #666;">暂无卡片,快去抽卡吧!</p>';
return;
}
// 遍历每个分类,生成折叠卡片组
for (const [category, cards] of Object.entries(categoryCards)) {
const categoryHtml = `
<div class="category-group">
<!-- 分类标题 -->
<div class="category-header" onclick="toggleCategory(this)">
<span>${category}</span>
<span></span> <!-- 折叠/展开箭头 -->
</div>
<!-- 分类下的卡片列表 -->
<div class="category-cards">
${cards.map(card => `
<div class="card-item">
<img src="${card.imageUrl}" alt="${card.name}">
<p>${card.name}</p>
<p class="time">${card.collectedAt}</p>
</div>
`).join('')}
</div>
</div>
`;
container.innerHTML += categoryHtml;
}
} catch (error) {
document.getElementById('categoryContainer').innerHTML =
'<p style="text-align: center; color: #ff4444;">加载失败,请刷新页面</p>';
}
};
// 折叠/展开分类卡片
function toggleCategory(header) {
const cardsList = header.nextElementSibling; // 卡片列表容器
const arrow = header.querySelector('span:last-child'); // 箭头元素
if (cardsList.style.display === 'grid') {
// 折叠:隐藏卡片列表,箭头变“▼”
cardsList.style.display = 'none';
arrow.textContent = '▼';
} else {
// 展开:显示卡片列表,箭头变“▲”
cardsList.style.display = 'grid';
arrow.textContent = '▲';
}
}
</script>
{% endblock %}

@ -0,0 +1,435 @@
{% extends "base.html" %}
{% block title %}{% if view_only %}查看日记 - AI日记{% else %}创作日记 - AI日记{% endif %}{% endblock %}
{% block extra_css %}
<style>
.diary-editor {
min-height: 300px;
transition: all 0.3s ease;
}
.diary-editor:focus {
min-height: 400px;
}
.tag-item {
cursor: pointer;
transition: all 0.2s ease;
}
.tag-item:hover {
transform: scale(1.05);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* 设置非编辑区域文字为不可选中 */
.card-body {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* 确保编辑区域和输入框可以选中和编辑 */
.diary-editor,
.form-control,
#content {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
/* 确保标签项可以点击但不会导致文本被选中 */
.tag-item {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* 确保按钮可以点击但不会导致文本被选中 */
button {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
</style>
{% endblock %}
{% block content %}
<div class="container py-8">
<div class="row">
<div class="col-lg-8 mx-auto fade-in">
<div class="card card-enhanced">
<div class="card-header bg-white">
<h3 class="mb-0">
{% if view_only %}
<i class="fas fa-eye text-primary-yellow me-2"></i>查看日记
{% else %}
<i class="fas fa-pen-to-square text-primary-yellow me-2"></i>记录今日心情
{% endif %}
</h3>
</div>
<div class="card-body">
<form method="POST">
{% if diary %}
<input type="hidden" name="diary_id" value="{{ diary[0] }}">
{% endif %}
<div class="mb-4">
<label for="title" class="form-label">标题</label>
<input type="text" class="form-control focus-yellow" id="title" name="title"
placeholder="给你的日记起个标题吧"
{% if diary %}value="{{ diary[1] }}"{% endif %}
{% if view_only %}readonly{% endif %} required>
</div>
<div class="mb-4">
<div class="d-flex justify-content-between items-center mb-2">
<label for="content" class="form-label">内容</label>
{% if not view_only %}
<button type="button" id="insert-card-btn" class="btn btn-light-yellow text-gray-800 btn-sm">
<i class="fas fa-image me-1"></i>插入卡片
</button>
{% endif %}
</div>
{% if view_only %}
<div class="form-control diary-editor focus-yellow" id="content-view" style="min-height: 300px; padding: 10px;">
{{ diary[2] | safe }}
</div>
{% else %}
<div id="content" name="content" class="form-control diary-editor focus-yellow"
style="min-height: 300px; padding: 10px; outline: none; overflow-y: auto;" contenteditable="true">
{% if diary %}{{ diary[2] | safe }}{% else %}
<p class="initial-placeholder">今天发生了什么?心情如何?</p>
{% endif %}
</div>
<input type="hidden" id="content-html" name="content">
{% endif %}
</div>
{% if not view_only %}
<div class="mb-4">
<label for="tags" class="form-label">添加标签</label>
<div class="mb-2">
<input type="text" class="form-control focus-yellow" id="tags" name="tags"
placeholder="输入标签,用逗号分隔"
{% if diary and diary[3] %}value="{{ diary[3] }}"{% endif %}>
</div>
<div class="d-flex flex-wrap gap-2">
<!-- 情绪标签 -->
<span class="badge bg-primary-yellow tag-item px-3 py-1 text-gray-800">开心</span>
<span class="badge bg-blue-100 tag-item px-3 py-1 text-blue-800">难过</span>
<span class="badge bg-green-100 tag-item px-3 py-1 text-green-800">平静</span>
<span class="badge bg-purple-100 tag-item px-3 py-1 text-purple-800">感恩</span>
<span class="badge bg-red-100 tag-item px-3 py-1 text-red-800">焦虑</span>
<span class="badge bg-orange-100 tag-item px-3 py-1 text-orange-800">生气</span>
<span class="badge bg-pink-100 tag-item px-3 py-1 text-pink-800">兴奋</span>
<span class="badge bg-indigo-100 tag-item px-3 py-1 text-indigo-800">思考</span>
<span class="badge bg-cyan-100 tag-item px-3 py-1 text-cyan-800">好奇</span>
<span class="badge bg-gray-100 tag-item px-3 py-1 text-gray-800">疲惫</span>
<!-- 活动标签 -->
<span class="badge bg-secondary-yellow tag-item px-3 py-1 text-gray-800">工作</span>
<span class="badge bg-green-200 tag-item px-3 py-1 text-green-800">学习</span>
<span class="badge bg-blue-200 tag-item px-3 py-1 text-blue-800">运动</span>
<span class="badge bg-red-200 tag-item px-3 py-1 text-red-800">娱乐</span>
<span class="badge bg-purple-200 tag-item px-3 py-1 text-purple-800">阅读</span>
<span class="badge bg-yellow-200 tag-item px-3 py-1 text-yellow-800">旅行</span>
<span class="badge bg-orange-200 tag-item px-3 py-1 text-orange-800">美食</span>
<span class="badge bg-pink-200 tag-item px-3 py-1 text-pink-800">社交</span>
<!-- 场景标签 -->
<span class="badge bg-gray-200 tag-item px-3 py-1 text-gray-800">早晨</span>
<span class="badge bg-yellow-300 tag-item px-3 py-1 text-yellow-800">中午</span>
<span class="badge bg-orange-300 tag-item px-3 py-1 text-orange-800">晚上</span>
<span class="badge bg-blue-300 tag-item px-3 py-1 text-blue-800">居家</span>
<span class="badge bg-green-300 tag-item px-3 py-1 text-green-800">户外</span>
<span class="badge bg-purple-300 tag-item px-3 py-1 text-purple-800">特殊</span>
<!-- 自定义标签 -->
<span class="badge bg-light-yellow tag-item px-3 py-1 text-gray-800">+ 自定义</span>
</div>
</div>
{% endif %}
<div class="d-flex justify-content-between">
<a href="{{ url_for('diary_list') }}" class="btn btn-light text-gray-800 btn-feedback">
<i class="fas fa-arrow-left me-1"></i>返回列表
</a>
{% if not view_only %}
<button type="submit" class="btn btn-primary-yellow text-gray-800 btn-feedback">
<i class="fas fa-paper-plane me-1"></i>{% if diary %}更新{% else %}提交{% endif %}日记
</button>
{% endif %}
</div>
</form>
</div>
</div>
<!-- 卡片选择模态框 - 移到表单外部 -->
<div class="modal" id="cardModal" style="display: none;">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="cardModalLabel">选择要插入的卡片</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="closeCardModal()"></button>
</div>
<div class="modal-body">
<div class="row" id="card-container">
<!-- 卡片将通过JavaScript动态加载 -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// 标签选择功能
document.querySelectorAll('.tag-item').forEach(tag => {
tag.addEventListener('click', function() {
const tagText = this.textContent.trim();
const tagsInput = document.getElementById('tags');
const currentTags = tagsInput.value.split(',').map(t => t.trim()).filter(t => t);
if (tagText === '+ 自定义') {
const newTag = prompt('请输入自定义标签:');
if (newTag && !currentTags.includes(newTag)) {
currentTags.push(newTag);
tagsInput.value = currentTags.join(', ');
}
return;
}
const index = currentTags.indexOf(tagText);
if (index > -1) {
currentTags.splice(index, 1);
this.classList.remove('bg-dark', 'text-white');
} else {
currentTags.push(tagText);
this.classList.add('bg-dark', 'text-white');
}
tagsInput.value = currentTags.join(', ');
});
});
// 页面加载时初始化标签状态
document.addEventListener('DOMContentLoaded', function() {
const tagsInput = document.getElementById('tags');
if (tagsInput.value) {
const currentTags = tagsInput.value.split(',').map(t => t.trim()).filter(t => t);
document.querySelectorAll('.tag-item').forEach(tag => {
const tagText = tag.textContent.trim();
if (currentTags.includes(tagText)) {
tag.classList.add('bg-dark', 'text-white');
}
});
}
// 插入卡片按钮事件
const insertCardBtn = document.getElementById('insert-card-btn');
const initDogtestBtn = document.getElementById('init-dogtest-btn');
if (insertCardBtn) {
insertCardBtn.addEventListener('click', function() {
// 显示模态框
document.getElementById('cardModal').style.display = 'block';
// 加载用户收藏的卡片
loadUserCards();
});
}
});
// 关闭卡片模态框
function closeCardModal() {
document.getElementById('cardModal').style.display = 'none';
}
// 加载用户收藏的卡片
function loadUserCards() {
const cardContainer = document.getElementById('card-container');
cardContainer.innerHTML = '<div class="col-12 text-center py-4"><i class="fas fa-spinner fa-spin mr-2"></i> 加载中...</div>';
fetch('/get_user_cards')
.then(response => {
if (!response.ok) {
throw new Error('网络响应不正常');
}
return response.json();
})
.then(data => {
cardContainer.innerHTML = '';
if (data.length === 0) {
cardContainer.innerHTML = '<div class="col-12 text-center py-4 text-muted">您还没有收集任何卡片,快去抽卡吧!</div>';
return;
}
// 按目录组织图片
const cardsByDirectory = {};
data.forEach(card => {
if (!cardsByDirectory[card.directory]) {
cardsByDirectory[card.directory] = [];
}
cardsByDirectory[card.directory].push(card);
});
// 遍历每个目录
Object.keys(cardsByDirectory).forEach(directory => {
// 创建目录标题
const dirTitle = document.createElement('div');
dirTitle.className = 'col-12 mt-4 mb-2';
dirTitle.innerHTML = `<h4 class="text-gray-700">${directory}</h4>`;
cardContainer.appendChild(dirTitle);
// 显示该目录下的所有卡片
cardsByDirectory[directory].forEach(card => {
const cardEl = document.createElement('div');
cardEl.className = 'col-md-3 mb-4 cursor-pointer hover-shadow';
cardEl.innerHTML = `
<div class="card h-100" data-image-url="${card.imageUrl}" data-card-name="${card.name}">
<img src="${card.imageUrl}" class="card-img-top" alt="${card.name}" style="height: 200px; object-fit: contain;">
<div class="card-body text-center">
<h5 class="card-title">${card.name}</h5>
</div>
</div>
`;
// 添加点击事件
cardEl.querySelector('.card').addEventListener('click', function() {
const imageUrl = this.getAttribute('data-image-url');
const cardName = this.getAttribute('data-card-name');
insertCardIntoContent(imageUrl, cardName);
closeCardModal();
});
cardContainer.appendChild(cardEl);
});
});
})
.catch(error => {
console.error('加载卡片失败:', error);
cardContainer.innerHTML = '<div class="col-12 text-center py-4 text-danger">加载卡片失败:' + error.message + '</div>';
});
}
// 将卡片插入到日记内容中
function insertCardIntoContent(imageUrl, cardName) {
const contentDiv = document.getElementById('content');
// 创建图片元素
const img = document.createElement('img');
img.src = imageUrl;
img.alt = cardName;
img.style.width = '80px'; // 大约四个汉字的长度
img.style.height = '80px'; // 大约四个汉字的长度
img.style.verticalAlign = 'middle';
img.style.margin = '0 2px';
// 获取当前选中区域
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
// 删除选中的内容
range.deleteContents();
// 插入图片
range.insertNode(img);
// 在图片后插入一个空格,方便继续输入
const space = document.createTextNode(' ');
range.insertNode(space);
// 将光标移动到图片后面
range.setStartAfter(space);
range.setEndAfter(space);
selection.removeAllRanges();
selection.addRange(range);
} else {
// 如果没有选中区域,就添加到内容末尾
contentDiv.appendChild(img);
contentDiv.appendChild(document.createTextNode(' '));
}
// 聚焦回内容区域
contentDiv.focus();
}
// 处理表单提交将富文本内容转换为HTML
document.addEventListener('DOMContentLoaded', function() {
const form = document.querySelector('form');
if (form) {
form.addEventListener('submit', function(e) {
const contentDiv = document.getElementById('content');
const contentHtmlInput = document.getElementById('content-html');
if (contentHtmlInput && contentDiv) {
// 移除初始占位符文本
const placeholder = contentDiv.querySelector('.initial-placeholder');
if (placeholder && contentDiv.contains(placeholder)) {
contentHtmlInput.value = '';
} else {
contentHtmlInput.value = contentDiv.innerHTML;
}
}
});
}
// 处理初始占位符点击事件
const placeholder = document.querySelector('.initial-placeholder');
const contentDiv = document.getElementById('content');
if (placeholder && contentDiv) {
// 当鼠标点击内容区域时移除占位符
contentDiv.addEventListener('mousedown', function(e) {
if (placeholder && contentDiv.contains(placeholder)) {
// 阻止事件冒泡,防止点击占位符时触发两次
e.stopPropagation();
// 移除占位符
placeholder.remove();
// 聚焦到内容区域
contentDiv.focus();
}
});
// 当占位符被点击时也移除占位符
placeholder.addEventListener('mousedown', function(e) {
e.stopPropagation();
this.remove();
contentDiv.focus();
});
}
});
// 添加悬停阴影效果
document.head.insertAdjacentHTML('beforeend', `
<style>
.cursor-pointer {
cursor: pointer;
}
.hover-shadow:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: translateY(-2px);
transition: all 0.2s ease;
}
</style>
`);
</script>
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save