mistpe commited on
Commit
97514f5
·
verified ·
1 Parent(s): 6cf62f8

Upload 2 files

Browse files
app/templates/admin/dashboard.html ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}管理面板 - 个人博客{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="dashboard-container" style="
7
+ max-width: 1200px;
8
+ margin: 0 auto;
9
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.8));
10
+ backdrop-filter: blur(10px);
11
+ border-radius: 24px;
12
+ box-shadow: 0 8px 32px rgba(99, 145, 197, 0.15);
13
+ border: 1px solid rgba(99, 145, 197, 0.2);
14
+ padding: 2rem;
15
+ ">
16
+ <div class="dashboard-header" style="
17
+ display: flex;
18
+ justify-content: space-between;
19
+ align-items: center;
20
+ margin-bottom: 2rem;
21
+ padding-bottom: 1.5rem;
22
+ border-bottom: 2px solid #B3CFEF;
23
+ ">
24
+ <div style="display: flex; align-items: center; gap: 1rem;">
25
+ <i class="fas fa-chart-line" style="
26
+ font-size: 2rem;
27
+ color: #6391C5;
28
+ "></i>
29
+ <h2 style="
30
+ font-size: 1.75rem;
31
+ font-weight: 600;
32
+ color: #6391C5;
33
+ margin: 0;
34
+ ">文章管理</h2>
35
+ </div>
36
+
37
+ <div class="dashboard-actions" style="
38
+ display: flex;
39
+ gap: 1rem;
40
+ ">
41
+ <button onclick="location.href='{{ url_for('admin.editor') }}'" style="
42
+ padding: 0.75rem 1.5rem;
43
+ background: linear-gradient(135deg, #6391C5, #C5CDFD);
44
+ color: white;
45
+ border: none;
46
+ border-radius: 12px;
47
+ font-weight: 500;
48
+ cursor: pointer;
49
+ transition: all 0.3s ease;
50
+ display: flex;
51
+ align-items: center;
52
+ gap: 0.5rem;
53
+ ">
54
+ <i class="fas fa-plus"></i>
55
+ 新建文章
56
+ </button>
57
+
58
+ <button onclick="exportData()" style="
59
+ padding: 0.75rem 1.5rem;
60
+ background: rgba(99, 145, 197, 0.1);
61
+ color: #6391C5;
62
+ border: 2px solid #B3CFEF;
63
+ border-radius: 12px;
64
+ font-weight: 500;
65
+ cursor: pointer;
66
+ transition: all 0.3s ease;
67
+ display: flex;
68
+ align-items: center;
69
+ gap: 0.5rem;
70
+ ">
71
+ <i class="fas fa-download"></i>
72
+ 导出数据
73
+ </button>
74
+
75
+ <button onclick="document.getElementById('importInput').click()" style="
76
+ padding: 0.75rem 1.5rem;
77
+ background: rgba(99, 145, 197, 0.1);
78
+ color: #6391C5;
79
+ border: 2px solid #B3CFEF;
80
+ border-radius: 12px;
81
+ font-weight: 500;
82
+ cursor: pointer;
83
+ transition: all 0.3s ease;
84
+ display: flex;
85
+ align-items: center;
86
+ gap: 0.5rem;
87
+ ">
88
+ <i class="fas fa-upload"></i>
89
+ 导入数据
90
+ </button>
91
+ <input type="file" id="importInput" style="display: none" accept=".db">
92
+ </div>
93
+ </div>
94
+
95
+ <div class="articles-table" style="
96
+ background: white;
97
+ border-radius: 16px;
98
+ box-shadow: 0 4px 16px rgba(99, 145, 197, 0.1);
99
+ overflow: hidden;
100
+ ">
101
+ <table style="width: 100%; border-collapse: collapse;">
102
+ <thead>
103
+ <tr style="background: #FEEEDA;">
104
+ <th style="
105
+ text-align: left;
106
+ padding: 1rem 1.5rem;
107
+ color: #6391C5;
108
+ font-weight: 600;
109
+ border-bottom: 2px solid #B3CFEF;
110
+ ">标题</th>
111
+ <th style="
112
+ text-align: left;
113
+ padding: 1rem 1.5rem;
114
+ color: #6391C5;
115
+ font-weight: 600;
116
+ border-bottom: 2px solid #B3CFEF;
117
+ ">创建时间</th>
118
+ <th style="
119
+ text-align: left;
120
+ padding: 1rem 1.5rem;
121
+ color: #6391C5;
122
+ font-weight: 600;
123
+ border-bottom: 2px solid #B3CFEF;
124
+ ">更新时间</th>
125
+ <th style="
126
+ text-align: left;
127
+ padding: 1rem 1.5rem;
128
+ color: #6391C5;
129
+ font-weight: 600;
130
+ border-bottom: 2px solid #B3CFEF;
131
+ ">操作</th>
132
+ </tr>
133
+ </thead>
134
+ <tbody>
135
+ {% for article in articles %}
136
+ <tr style="transition: all 0.3s ease;">
137
+ <td style="
138
+ padding: 1rem 1.5rem;
139
+ border-bottom: 1px solid #B3CFEF;
140
+ color: #6391C5;
141
+ ">{{ article.title }}</td>
142
+ <td style="
143
+ padding: 1rem 1.5rem;
144
+ border-bottom: 1px solid #B3CFEF;
145
+ color: #6391C5;
146
+ ">{{ article.created_at.strftime('%Y-%m-%d') }}</td>
147
+ <td style="
148
+ padding: 1rem 1.5rem;
149
+ border-bottom: 1px solid #B3CFEF;
150
+ color: #6391C5;
151
+ ">{{ article.updated_at.strftime('%Y-%m-%d') }}</td>
152
+ <td style="
153
+ padding: 1rem 1.5rem;
154
+ border-bottom: 1px solid #B3CFEF;
155
+ ">
156
+ <div style="display: flex; gap: 0.5rem;">
157
+ <a href="{{ url_for('admin.editor', slug=article.slug) }}" style="
158
+ padding: 0.5rem 1rem;
159
+ background: linear-gradient(135deg, #6391C5, #C5CDFD);
160
+ color: white;
161
+ border-radius: 8px;
162
+ text-decoration: none;
163
+ font-size: 0.875rem;
164
+ transition: all 0.3s ease;
165
+ ">编辑</a>
166
+ <button onclick="deleteArticle('{{ article.slug }}')" style="
167
+ padding: 0.5rem 1rem;
168
+ background: rgba(255, 71, 87, 0.1);
169
+ color: #ff4757;
170
+ border: 1px solid rgba(255, 71, 87, 0.2);
171
+ border-radius: 8px;
172
+ font-size: 0.875rem;
173
+ cursor: pointer;
174
+ transition: all 0.3s ease;
175
+ ">删除</button>
176
+ </div>
177
+ </td>
178
+ </tr>
179
+ {% endfor %}
180
+ </tbody>
181
+ </table>
182
+ </div>
183
+ </div>
184
+
185
+ <script>
186
+ // 表格行悬停效果
187
+ document.querySelectorAll('tbody tr').forEach(row => {
188
+ row.addEventListener('mouseenter', () => {
189
+ row.style.background = 'rgba(99, 145, 197, 0.05)';
190
+ });
191
+ row.addEventListener('mouseleave', () => {
192
+ row.style.background = 'white';
193
+ });
194
+ });
195
+
196
+ // 按钮悬停效果
197
+ document.querySelectorAll('button').forEach(button => {
198
+ button.addEventListener('mouseenter', () => {
199
+ if (button.style.background.includes('linear-gradient')) {
200
+ button.style.transform = 'translateY(-2px)';
201
+ button.style.boxShadow = '0 4px 12px rgba(99, 145, 197, 0.2)';
202
+ } else if (button.style.background.includes('rgba(255, 71, 87')) {
203
+ button.style.background = 'rgba(255, 71, 87, 0.2)';
204
+ } else {
205
+ button.style.borderColor = '#6391C5';
206
+ button.style.background = 'rgba(99, 145, 197, 0.15)';
207
+ }
208
+ });
209
+
210
+ button.addEventListener('mouseleave', () => {
211
+ if (button.style.background.includes('linear-gradient')) {
212
+ button.style.transform = 'none';
213
+ button.style.boxShadow = 'none';
214
+ } else if (button.style.background.includes('rgba(255, 71, 87')) {
215
+ button.style.background = 'rgba(255, 71, 87, 0.1)';
216
+ } else {
217
+ button.style.borderColor = '#B3CFEF';
218
+ button.style.background = 'rgba(99, 145, 197, 0.1)';
219
+ }
220
+ });
221
+ });
222
+
223
+ // 编辑链接悬停效果
224
+ document.querySelectorAll('a').forEach(link => {
225
+ if (link.style.background.includes('linear-gradient')) {
226
+ link.addEventListener('mouseenter', () => {
227
+ link.style.transform = 'translateY(-2px)';
228
+ link.style.boxShadow = '0 4px 12px rgba(99, 145, 197, 0.2)';
229
+ });
230
+
231
+ link.addEventListener('mouseleave', () => {
232
+ link.style.transform = 'none';
233
+ link.style.boxShadow = 'none';
234
+ });
235
+ }
236
+ });
237
+
238
+ async function deleteArticle(slug) {
239
+ if (!confirm('确定要删除这篇文章吗?')) return;
240
+
241
+ try {
242
+ const response = await fetch(`/api/articles/${slug}`, {
243
+ method: 'DELETE'
244
+ });
245
+
246
+ if (response.ok) {
247
+ location.reload();
248
+ }
249
+ } catch (error) {
250
+ console.error('Error:', error);
251
+ alert('删除失败,请重试');
252
+ }
253
+ }
254
+
255
+ async function exportData() {
256
+ try {
257
+ window.location.href = '/api/export';
258
+ } catch (error) {
259
+ console.error('Error:', error);
260
+ alert('导出失败,请重试');
261
+ }
262
+ }
263
+
264
+ document.getElementById('importInput').addEventListener('change', async (event) => {
265
+ const file = event.target.files[0];
266
+ if (!file) return;
267
+
268
+ try {
269
+ const formData = new FormData();
270
+ formData.append('file', file);
271
+
272
+ const response = await fetch('/api/import', {
273
+ method: 'POST',
274
+ body: formData
275
+ });
276
+
277
+ const data = await response.json();
278
+ if (response.ok) {
279
+ alert('导入成功,页面将刷新');
280
+ location.reload();
281
+ } else {
282
+ throw new Error(data.error || '导入失败');
283
+ }
284
+ } catch (error) {
285
+ console.error('Error:', error);
286
+ alert('导入失败:' + error.message);
287
+ }
288
+ });
289
+ </script>
290
+ {% endblock %}
app/templates/admin/login.html ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+
3
+ {% block title %}管理员登录 - 个人博客{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="login-container" style="
7
+ min-height: calc(100vh - 8rem);
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ padding: 2rem;
12
+ background: linear-gradient(135deg, #FEEEDA 0%, #B3CFEF 100%);
13
+ ">
14
+ <div class="login-form" style="
15
+ width: 100%;
16
+ max-width: 420px;
17
+ background: linear-gradient(135deg, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.8));
18
+ backdrop-filter: blur(10px);
19
+ padding: 3rem;
20
+ border-radius: 24px;
21
+ box-shadow: 0 8px 32px rgba(99, 145, 197, 0.15);
22
+ border: 1px solid rgba(99, 145, 197, 0.2);
23
+ ">
24
+ <div class="form-header" style="
25
+ text-align: center;
26
+ margin-bottom: 2.5rem;
27
+ ">
28
+ <div class="logo-container" style="
29
+ margin-bottom: 1.5rem;
30
+ ">
31
+ <i class="fas fa-feather" style="
32
+ font-size: 3rem;
33
+ background: linear-gradient(135deg, #6391C5, #C5CDFD);
34
+ -webkit-background-clip: text;
35
+ -webkit-text-fill-color: transparent;
36
+ "></i>
37
+ </div>
38
+ <h2 style="
39
+ font-size: 1.75rem;
40
+ font-weight: 600;
41
+ color: #6391C5;
42
+ margin-bottom: 0.5rem;
43
+ ">管理员登录</h2>
44
+ <p style="
45
+ color: #6391C5;
46
+ font-size: 0.9375rem;
47
+ ">请输入您的管理员账号密码</p>
48
+ </div>
49
+
50
+ <form method="POST" style="display: flex; flex-direction: column; gap: 1.5rem;">
51
+ {% with messages = get_flashed_messages() %}
52
+ {% if messages %}
53
+ <div style="
54
+ background: rgba(99, 145, 197, 0.1);
55
+ border: 1px solid #B3CFEF;
56
+ padding: 1rem;
57
+ border-radius: 12px;
58
+ color: #6391C5;
59
+ font-size: 0.875rem;
60
+ text-align: center;
61
+ ">
62
+ {{ messages[0] }}
63
+ </div>
64
+ {% endif %}
65
+ {% endwith %}
66
+
67
+ <div class="form-group" style="position: relative;">
68
+ <div style="
69
+ position: absolute;
70
+ left: 1rem;
71
+ top: 50%;
72
+ transform: translateY(-50%);
73
+ color: #6391C5;
74
+ ">
75
+ <i class="fas fa-user"></i>
76
+ </div>
77
+ <input type="text" name="username" placeholder="用户名" required style="
78
+ width: 100%;
79
+ padding: 1rem 1rem 1rem 2.75rem;
80
+ border: 2px solid #B3CFEF;
81
+ border-radius: 12px;
82
+ font-size: 1rem;
83
+ transition: all 0.3s ease;
84
+ color: #6391C5;
85
+ background: rgba(255, 255, 255, 0.7);
86
+ ">
87
+ </div>
88
+
89
+ <div class="form-group" style="position: relative;">
90
+ <div style="
91
+ position: absolute;
92
+ left: 1rem;
93
+ top: 50%;
94
+ transform: translateY(-50%);
95
+ color: #6391C5;
96
+ ">
97
+ <i class="fas fa-lock"></i>
98
+ </div>
99
+ <input type="password" name="password" placeholder="密码" required style="
100
+ width: 100%;
101
+ padding: 1rem 1rem 1rem 2.75rem;
102
+ border: 2px solid #B3CFEF;
103
+ border-radius: 12px;
104
+ font-size: 1rem;
105
+ transition: all 0.3s ease;
106
+ color: #6391C5;
107
+ background: rgba(255, 255, 255, 0.7);
108
+ ">
109
+ </div>
110
+
111
+ <button type="submit" style="
112
+ width: 100%;
113
+ padding: 1rem;
114
+ background: linear-gradient(135deg, #6391C5, #C5CDFD);
115
+ color: white;
116
+ border: none;
117
+ border-radius: 12px;
118
+ font-size: 1rem;
119
+ font-weight: 500;
120
+ cursor: pointer;
121
+ transition: all 0.3s ease;
122
+ margin-top: 1rem;
123
+ ">登录</button>
124
+
125
+ <div style="
126
+ text-align: center;
127
+ margin-top: 1.5rem;
128
+ font-size: 0.875rem;
129
+ ">
130
+ <a href="{{ url_for('main.index') }}" style="
131
+ color: #6391C5;
132
+ text-decoration: none;
133
+ transition: color 0.3s ease;
134
+ ">
135
+ <i class="fas fa-arrow-left" style="margin-right: 0.5rem;"></i>返回首页
136
+ </a>
137
+ </div>
138
+ </form>
139
+ </div>
140
+ </div>
141
+
142
+ <script>
143
+ document.querySelectorAll('input').forEach(input => {
144
+ input.addEventListener('focus', () => {
145
+ input.style.borderColor = '#6391C5';
146
+ input.style.background = 'rgba(255, 255, 255, 0.9)';
147
+ input.style.boxShadow = '0 0 0 4px rgba(99, 145, 197, 0.1)';
148
+ });
149
+
150
+ input.addEventListener('blur', () => {
151
+ input.style.borderColor = '#B3CFEF';
152
+ input.style.background = 'rgba(255, 255, 255, 0.7)';
153
+ input.style.boxShadow = 'none';
154
+ });
155
+ });
156
+
157
+ document.querySelector('button[type="submit"]').addEventListener('mouseenter', function() {
158
+ this.style.transform = 'translateY(-2px)';
159
+ this.style.boxShadow = '0 4px 12px rgba(99, 145, 197, 0.2)';
160
+ });
161
+
162
+ document.querySelector('button[type="submit"]').addEventListener('mouseleave', function() {
163
+ this.style.transform = 'none';
164
+ this.style.boxShadow = 'none';
165
+ });
166
+
167
+ document.querySelector('a').addEventListener('mouseenter', function() {
168
+ this.style.color = '#C5CDFD';
169
+ });
170
+
171
+ document.querySelector('a').addEventListener('mouseleave', function() {
172
+ this.style.color = '#6391C5';
173
+ });
174
+ </script>
175
+ {% endblock %}