mistpe commited on
Commit
6259b7a
·
verified ·
1 Parent(s): fc2bafb

Update app/templates/index.html

Browse files
Files changed (1) hide show
  1. app/templates/index.html +351 -351
app/templates/index.html CHANGED
@@ -1,352 +1,352 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}首页 - 个人博客{% endblock %}
4
-
5
- {% block content %}
6
- <!-- 搜索区域 -->
7
- <section class="search-container">
8
- <div class="search-wrapper">
9
- <input
10
- type="text"
11
- class="search-input"
12
- placeholder="搜索文章..."
13
- id="searchInput"
14
- >
15
- <i class="fas fa-search search-icon"></i>
16
- <button class="search-reset" id="searchReset" style="display: none;">
17
- <i class="fas fa-times"></i>
18
- </button>
19
- </div>
20
- </section>
21
-
22
- <!-- 文章列表 -->
23
- <section class="articles-section">
24
- <div class="section-header">
25
- <h1 class="section-title">所有文章</h1>
26
- <div class="view-controls">
27
- <button class="view-btn" data-view="list">
28
- <i class="fas fa-list"></i>
29
- 列表视图
30
- </button>
31
- <button class="view-btn active" data-view="grid">
32
- <i class="fas fa-th-large"></i>
33
- 卡片视图
34
- </button>
35
- </div>
36
- </div>
37
-
38
- <div class="articles-container grid-view" id="articlesContainer">
39
- {% for article in articles %}
40
- <article class="article-card">
41
- <div class="article-icon">
42
- <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
43
- <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
44
- <polyline points="14 2 14 8 20 8"></polyline>
45
- <line x1="16" y1="13" x2="8" y2="13"></line>
46
- <line x1="16" y1="17" x2="8" y2="17"></line>
47
- <line x1="10" y1="9" x2="8" y2="9"></line>
48
- </svg>
49
- </div>
50
- <div class="article-content">
51
- <h2 class="article-title">
52
- <a href="{{ url_for('main.article', slug=article.slug) }}">
53
- {{ article.title }}
54
- </a>
55
- </h2>
56
- {% if article.summary %}
57
- <div class="article-summary">
58
- {{ article.summary }}
59
- </div>
60
- {% endif %}
61
- <div class="article-meta">
62
- <span class="meta-date">
63
- <i class="fas fa-calendar"></i>
64
- {{ article.created_at.strftime('%Y-%m-%d') }}
65
- </span>
66
- </div>
67
- </div>
68
- </article>
69
- {% endfor %}
70
- </div>
71
- </section>
72
-
73
- <style>
74
- .search-container {
75
- margin-bottom: 2rem;
76
- }
77
-
78
- .search-wrapper {
79
- position: relative;
80
- max-width: 800px;
81
- margin: 0 auto;
82
- }
83
-
84
- .search-input {
85
- width: 100%;
86
- padding: 1rem 1.25rem 1rem 3rem;
87
- border: 2px solid var(--light-blue);
88
- border-radius: 12px;
89
- background: white;
90
- font-size: 1rem;
91
- transition: all 0.3s ease;
92
- color: var(--text-dark);
93
- padding-right: 3rem;
94
- }
95
-
96
- .search-input:focus {
97
- outline: none;
98
- border-color: var(--primary-blue);
99
- box-shadow: 0 0 0 4px rgba(99, 145, 197, 0.1);
100
- }
101
-
102
- .search-icon {
103
- position: absolute;
104
- left: 1.25rem;
105
- top: 50%;
106
- transform: translateY(-50%);
107
- color: var(--primary-blue);
108
- }
109
-
110
- .search-reset {
111
- position: absolute;
112
- right: 1rem;
113
- top: 50%;
114
- transform: translateY(-50%);
115
- background: none;
116
- border: none;
117
- color: var(--primary-blue);
118
- cursor: pointer;
119
- padding: 0.5rem;
120
- border-radius: 50%;
121
- transition: all 0.3s ease;
122
- }
123
-
124
- .search-reset:hover {
125
- background: rgba(99, 145, 197, 0.1);
126
- }
127
-
128
- .section-header {
129
- margin-bottom: 2rem;
130
- display: flex;
131
- justify-content: space-between;
132
- align-items: center;
133
- }
134
-
135
- .section-title {
136
- font-size: 1.75rem;
137
- color: var(--text-dark);
138
- font-weight: 600;
139
- }
140
-
141
- .view-controls {
142
- display: flex;
143
- gap: 0.5rem;
144
- background: white;
145
- padding: 0.25rem;
146
- border-radius: 8px;
147
- border: 1px solid var(--light-blue);
148
- }
149
-
150
- .view-btn {
151
- display: flex;
152
- align-items: center;
153
- gap: 0.5rem;
154
- padding: 0.5rem 1rem;
155
- border: none;
156
- background: none;
157
- border-radius: 6px;
158
- color: var(--text-dark);
159
- cursor: pointer;
160
- transition: all 0.3s ease;
161
- }
162
-
163
- .view-btn.active {
164
- background: var(--primary-blue);
165
- color: white;
166
- }
167
-
168
- .view-btn i {
169
- font-size: 1rem;
170
- }
171
-
172
- /* 列表视图样式 */
173
- .articles-container.list-view {
174
- display: flex;
175
- flex-direction: column;
176
- gap: 1rem;
177
- }
178
-
179
- .list-view .article-card {
180
- background: white;
181
- border-radius: 12px;
182
- border: 1px solid var(--light-blue);
183
- padding: 1.5rem;
184
- display: flex;
185
- gap: 1.5rem;
186
- transition: all 0.3s ease;
187
- }
188
-
189
- /* 网格视图样式 */
190
- .articles-container.grid-view {
191
- display: grid;
192
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
193
- gap: 1.5rem;
194
- }
195
-
196
- .grid-view .article-card {
197
- background: white;
198
- border-radius: 12px;
199
- border: 1px solid var(--light-blue);
200
- padding: 1.5rem;
201
- display: flex;
202
- flex-direction: column;
203
- gap: 1rem;
204
- transition: all 0.3s ease;
205
- }
206
-
207
- .grid-view .article-icon {
208
- align-self: flex-start;
209
- }
210
-
211
- .article-card:hover {
212
- border-color: var(--primary-blue);
213
- box-shadow: 0 4px 12px rgba(99, 145, 197, 0.1);
214
- transform: translateY(-2px);
215
- }
216
-
217
- .article-icon {
218
- color: var(--primary-blue);
219
- flex-shrink: 0;
220
- }
221
-
222
- .article-content {
223
- flex: 1;
224
- }
225
-
226
- .article-title {
227
- font-size: 1.25rem;
228
- font-weight: 600;
229
- margin-bottom: 0.75rem;
230
- line-height: 1.4;
231
- }
232
-
233
- .article-title a {
234
- color: var(--text-dark);
235
- text-decoration: none;
236
- transition: color 0.3s ease;
237
- }
238
-
239
- .article-title a:hover {
240
- color: var(--primary-blue);
241
- }
242
-
243
- .article-summary {
244
- color: #64748B;
245
- margin-bottom: 1rem;
246
- line-height: 1.6;
247
- display: -webkit-box;
248
- -webkit-line-clamp: 2;
249
- -webkit-box-orient: vertical;
250
- overflow: hidden;
251
- }
252
-
253
- .article-meta {
254
- display: flex;
255
- align-items: center;
256
- gap: 1rem;
257
- color: #94A3B8;
258
- font-size: 0.875rem;
259
- }
260
-
261
- .meta-date {
262
- display: flex;
263
- align-items: center;
264
- gap: 0.5rem;
265
- }
266
-
267
- .meta-date i {
268
- color: var(--primary-blue);
269
- }
270
-
271
- @media (max-width: 640px) {
272
- .article-card {
273
- padding: 1rem;
274
- }
275
-
276
- .article-summary {
277
- -webkit-line-clamp: 3;
278
- }
279
-
280
- .articles-container.grid-view {
281
- grid-template-columns: 1fr;
282
- }
283
-
284
- .section-header {
285
- flex-direction: column;
286
- gap: 1rem;
287
- align-items: flex-start;
288
- }
289
- }
290
- </style>
291
-
292
- <script>
293
- const searchInput = document.getElementById('searchInput');
294
- const searchReset = document.getElementById('searchReset');
295
- const articlesContainer = document.getElementById('articlesContainer');
296
- const viewButtons = document.querySelectorAll('.view-btn');
297
- let allArticles = [...document.querySelectorAll('.article-card')];
298
-
299
- function debounce(func, wait) {
300
- let timeout;
301
- return function executedFunction(...args) {
302
- const later = () => {
303
- clearTimeout(timeout);
304
- func(...args);
305
- };
306
- clearTimeout(timeout);
307
- timeout = setTimeout(later, wait);
308
- };
309
- }
310
-
311
- function filterArticles(query) {
312
- query = query.toLowerCase().trim();
313
- searchReset.style.display = query ? 'block' : 'none';
314
-
315
- allArticles.forEach(article => {
316
- const title = article.querySelector('.article-title').textContent.toLowerCase();
317
- const summary = article.querySelector('.article-summary')?.textContent.toLowerCase() || '';
318
-
319
- if (title.includes(query) || summary.includes(query) || query === '') {
320
- article.style.display = '';
321
- } else {
322
- article.style.display = 'none';
323
- }
324
- });
325
- }
326
-
327
- // 视图切换
328
- viewButtons.forEach(button => {
329
- button.addEventListener('click', () => {
330
- viewButtons.forEach(btn => btn.classList.remove('active'));
331
- button.classList.add('active');
332
-
333
- const viewType = button.dataset.view;
334
- articlesContainer.className = `articles-container ${viewType}-view`;
335
- });
336
- });
337
-
338
- // 搜索功能
339
- const debouncedFilter = debounce(filterArticles, 300);
340
-
341
- searchInput.addEventListener('input', (e) => {
342
- debouncedFilter(e.target.value);
343
- });
344
-
345
- // 重置搜索
346
- searchReset.addEventListener('click', () => {
347
- searchInput.value = '';
348
- filterArticles('');
349
- searchInput.focus();
350
- });
351
- </script>
352
  {% endblock %}
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}首页 - Wisdom Hub{% endblock %}
4
+
5
+ {% block content %}
6
+ <!-- 搜索区域 -->
7
+ <section class="search-container">
8
+ <div class="search-wrapper">
9
+ <input
10
+ type="text"
11
+ class="search-input"
12
+ placeholder="搜索文章..."
13
+ id="searchInput"
14
+ >
15
+ <i class="fas fa-search search-icon"></i>
16
+ <button class="search-reset" id="searchReset" style="display: none;">
17
+ <i class="fas fa-times"></i>
18
+ </button>
19
+ </div>
20
+ </section>
21
+
22
+ <!-- 文章列表 -->
23
+ <section class="articles-section">
24
+ <div class="section-header">
25
+ <h1 class="section-title">所有文章</h1>
26
+ <div class="view-controls">
27
+ <button class="view-btn" data-view="list">
28
+ <i class="fas fa-list"></i>
29
+ 列表视图
30
+ </button>
31
+ <button class="view-btn active" data-view="grid">
32
+ <i class="fas fa-th-large"></i>
33
+ 卡片视图
34
+ </button>
35
+ </div>
36
+ </div>
37
+
38
+ <div class="articles-container grid-view" id="articlesContainer">
39
+ {% for article in articles %}
40
+ <article class="article-card">
41
+ <div class="article-icon">
42
+ <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
43
+ <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
44
+ <polyline points="14 2 14 8 20 8"></polyline>
45
+ <line x1="16" y1="13" x2="8" y2="13"></line>
46
+ <line x1="16" y1="17" x2="8" y2="17"></line>
47
+ <line x1="10" y1="9" x2="8" y2="9"></line>
48
+ </svg>
49
+ </div>
50
+ <div class="article-content">
51
+ <h2 class="article-title">
52
+ <a href="{{ url_for('main.article', slug=article.slug) }}">
53
+ {{ article.title }}
54
+ </a>
55
+ </h2>
56
+ {% if article.summary %}
57
+ <div class="article-summary">
58
+ {{ article.summary }}
59
+ </div>
60
+ {% endif %}
61
+ <div class="article-meta">
62
+ <span class="meta-date">
63
+ <i class="fas fa-calendar"></i>
64
+ {{ article.created_at.strftime('%Y-%m-%d') }}
65
+ </span>
66
+ </div>
67
+ </div>
68
+ </article>
69
+ {% endfor %}
70
+ </div>
71
+ </section>
72
+
73
+ <style>
74
+ .search-container {
75
+ margin-bottom: 2rem;
76
+ }
77
+
78
+ .search-wrapper {
79
+ position: relative;
80
+ max-width: 800px;
81
+ margin: 0 auto;
82
+ }
83
+
84
+ .search-input {
85
+ width: 100%;
86
+ padding: 1rem 1.25rem 1rem 3rem;
87
+ border: 2px solid var(--light-blue);
88
+ border-radius: 12px;
89
+ background: white;
90
+ font-size: 1rem;
91
+ transition: all 0.3s ease;
92
+ color: var(--text-dark);
93
+ padding-right: 3rem;
94
+ }
95
+
96
+ .search-input:focus {
97
+ outline: none;
98
+ border-color: var(--primary-blue);
99
+ box-shadow: 0 0 0 4px rgba(99, 145, 197, 0.1);
100
+ }
101
+
102
+ .search-icon {
103
+ position: absolute;
104
+ left: 1.25rem;
105
+ top: 50%;
106
+ transform: translateY(-50%);
107
+ color: var(--primary-blue);
108
+ }
109
+
110
+ .search-reset {
111
+ position: absolute;
112
+ right: 1rem;
113
+ top: 50%;
114
+ transform: translateY(-50%);
115
+ background: none;
116
+ border: none;
117
+ color: var(--primary-blue);
118
+ cursor: pointer;
119
+ padding: 0.5rem;
120
+ border-radius: 50%;
121
+ transition: all 0.3s ease;
122
+ }
123
+
124
+ .search-reset:hover {
125
+ background: rgba(99, 145, 197, 0.1);
126
+ }
127
+
128
+ .section-header {
129
+ margin-bottom: 2rem;
130
+ display: flex;
131
+ justify-content: space-between;
132
+ align-items: center;
133
+ }
134
+
135
+ .section-title {
136
+ font-size: 1.75rem;
137
+ color: var(--text-dark);
138
+ font-weight: 600;
139
+ }
140
+
141
+ .view-controls {
142
+ display: flex;
143
+ gap: 0.5rem;
144
+ background: white;
145
+ padding: 0.25rem;
146
+ border-radius: 8px;
147
+ border: 1px solid var(--light-blue);
148
+ }
149
+
150
+ .view-btn {
151
+ display: flex;
152
+ align-items: center;
153
+ gap: 0.5rem;
154
+ padding: 0.5rem 1rem;
155
+ border: none;
156
+ background: none;
157
+ border-radius: 6px;
158
+ color: var(--text-dark);
159
+ cursor: pointer;
160
+ transition: all 0.3s ease;
161
+ }
162
+
163
+ .view-btn.active {
164
+ background: var(--primary-blue);
165
+ color: white;
166
+ }
167
+
168
+ .view-btn i {
169
+ font-size: 1rem;
170
+ }
171
+
172
+ /* 列表视图样式 */
173
+ .articles-container.list-view {
174
+ display: flex;
175
+ flex-direction: column;
176
+ gap: 1rem;
177
+ }
178
+
179
+ .list-view .article-card {
180
+ background: white;
181
+ border-radius: 12px;
182
+ border: 1px solid var(--light-blue);
183
+ padding: 1.5rem;
184
+ display: flex;
185
+ gap: 1.5rem;
186
+ transition: all 0.3s ease;
187
+ }
188
+
189
+ /* 网格视图样式 */
190
+ .articles-container.grid-view {
191
+ display: grid;
192
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
193
+ gap: 1.5rem;
194
+ }
195
+
196
+ .grid-view .article-card {
197
+ background: white;
198
+ border-radius: 12px;
199
+ border: 1px solid var(--light-blue);
200
+ padding: 1.5rem;
201
+ display: flex;
202
+ flex-direction: column;
203
+ gap: 1rem;
204
+ transition: all 0.3s ease;
205
+ }
206
+
207
+ .grid-view .article-icon {
208
+ align-self: flex-start;
209
+ }
210
+
211
+ .article-card:hover {
212
+ border-color: var(--primary-blue);
213
+ box-shadow: 0 4px 12px rgba(99, 145, 197, 0.1);
214
+ transform: translateY(-2px);
215
+ }
216
+
217
+ .article-icon {
218
+ color: var(--primary-blue);
219
+ flex-shrink: 0;
220
+ }
221
+
222
+ .article-content {
223
+ flex: 1;
224
+ }
225
+
226
+ .article-title {
227
+ font-size: 1.25rem;
228
+ font-weight: 600;
229
+ margin-bottom: 0.75rem;
230
+ line-height: 1.4;
231
+ }
232
+
233
+ .article-title a {
234
+ color: var(--text-dark);
235
+ text-decoration: none;
236
+ transition: color 0.3s ease;
237
+ }
238
+
239
+ .article-title a:hover {
240
+ color: var(--primary-blue);
241
+ }
242
+
243
+ .article-summary {
244
+ color: #64748B;
245
+ margin-bottom: 1rem;
246
+ line-height: 1.6;
247
+ display: -webkit-box;
248
+ -webkit-line-clamp: 2;
249
+ -webkit-box-orient: vertical;
250
+ overflow: hidden;
251
+ }
252
+
253
+ .article-meta {
254
+ display: flex;
255
+ align-items: center;
256
+ gap: 1rem;
257
+ color: #94A3B8;
258
+ font-size: 0.875rem;
259
+ }
260
+
261
+ .meta-date {
262
+ display: flex;
263
+ align-items: center;
264
+ gap: 0.5rem;
265
+ }
266
+
267
+ .meta-date i {
268
+ color: var(--primary-blue);
269
+ }
270
+
271
+ @media (max-width: 640px) {
272
+ .article-card {
273
+ padding: 1rem;
274
+ }
275
+
276
+ .article-summary {
277
+ -webkit-line-clamp: 3;
278
+ }
279
+
280
+ .articles-container.grid-view {
281
+ grid-template-columns: 1fr;
282
+ }
283
+
284
+ .section-header {
285
+ flex-direction: column;
286
+ gap: 1rem;
287
+ align-items: flex-start;
288
+ }
289
+ }
290
+ </style>
291
+
292
+ <script>
293
+ const searchInput = document.getElementById('searchInput');
294
+ const searchReset = document.getElementById('searchReset');
295
+ const articlesContainer = document.getElementById('articlesContainer');
296
+ const viewButtons = document.querySelectorAll('.view-btn');
297
+ let allArticles = [...document.querySelectorAll('.article-card')];
298
+
299
+ function debounce(func, wait) {
300
+ let timeout;
301
+ return function executedFunction(...args) {
302
+ const later = () => {
303
+ clearTimeout(timeout);
304
+ func(...args);
305
+ };
306
+ clearTimeout(timeout);
307
+ timeout = setTimeout(later, wait);
308
+ };
309
+ }
310
+
311
+ function filterArticles(query) {
312
+ query = query.toLowerCase().trim();
313
+ searchReset.style.display = query ? 'block' : 'none';
314
+
315
+ allArticles.forEach(article => {
316
+ const title = article.querySelector('.article-title').textContent.toLowerCase();
317
+ const summary = article.querySelector('.article-summary')?.textContent.toLowerCase() || '';
318
+
319
+ if (title.includes(query) || summary.includes(query) || query === '') {
320
+ article.style.display = '';
321
+ } else {
322
+ article.style.display = 'none';
323
+ }
324
+ });
325
+ }
326
+
327
+ // 视图切换
328
+ viewButtons.forEach(button => {
329
+ button.addEventListener('click', () => {
330
+ viewButtons.forEach(btn => btn.classList.remove('active'));
331
+ button.classList.add('active');
332
+
333
+ const viewType = button.dataset.view;
334
+ articlesContainer.className = `articles-container ${viewType}-view`;
335
+ });
336
+ });
337
+
338
+ // 搜索功能
339
+ const debouncedFilter = debounce(filterArticles, 300);
340
+
341
+ searchInput.addEventListener('input', (e) => {
342
+ debouncedFilter(e.target.value);
343
+ });
344
+
345
+ // 重置搜索
346
+ searchReset.addEventListener('click', () => {
347
+ searchInput.value = '';
348
+ filterArticles('');
349
+ searchInput.focus();
350
+ });
351
+ </script>
352
  {% endblock %}