@flun/html-template 4.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/.env +9 -0
  2. package/LICENSE +15 -0
  3. package/build.js +3 -0
  4. package/compile.js +349 -0
  5. package/copy-files.js +200 -0
  6. package/customize/account.js +726 -0
  7. package/customize/data.json +484 -0
  8. package/customize/functions.js +48 -0
  9. package/customize/hotReloadInjector.js +25 -0
  10. package/customize/routes.js +141 -0
  11. package/customize/users.json +44 -0
  12. package/customize/variables.js +70 -0
  13. package/dev-server.js +344 -0
  14. package/dev.js +4 -0
  15. package/f-CHANGELOG.md +4 -0
  16. package/f-README.md +485 -0
  17. package/index.d.ts +133 -0
  18. package/index.js +4 -0
  19. package/package.json +77 -0
  20. package/restoreDefaults.js +8 -0
  21. package/services/templateService.js +962 -0
  22. package/static/about.css +118 -0
  23. package/static/auth.js +27 -0
  24. package/static/constants.css +138 -0
  25. package/static/img/dark.png +0 -0
  26. package/static/img/favicon.ico +0 -0
  27. package/static/img/light.png +0 -0
  28. package/static/img/top.png +0 -0
  29. package/static/index.css +86 -0
  30. package/static/mouseOrTouch.js +156 -0
  31. package/static/public.css +288 -0
  32. package/static/script.css +318 -0
  33. package/static/script.js +392 -0
  34. package/static/styling.css +874 -0
  35. package/static/styling.js +933 -0
  36. package/static/themeImg.css +10 -0
  37. package/static/themeImg.js +19 -0
  38. package/static/themeModule.js +222 -0
  39. package/static/topImg.css +19 -0
  40. package/static/topImg.js +21 -0
  41. package/static/utils/browser13.js +270 -0
  42. package/static/utils/closebrackets.js +166 -0
  43. package/static/utils/css-lint.js +308 -0
  44. package/static/utils/custom-css-hint.js +876 -0
  45. package/static/utils/foldgutter.js +141 -0
  46. package/static/utils/match-highlighter.js +70 -0
  47. package/templates/about.html +236 -0
  48. package/templates/account/2fa.html +184 -0
  49. package/templates/account/forgot-password.html +226 -0
  50. package/templates/account/login.html +230 -0
  51. package/templates/account/profile.html +977 -0
  52. package/templates/account/register.html +224 -0
  53. package/templates/account/reset-password.html +205 -0
  54. package/templates/account/verify-email.html +163 -0
  55. package/templates/base.html +71 -0
  56. package/templates/footer-content.html +5 -0
  57. package/templates/index.html +140 -0
  58. package/templates/script.html +209 -0
  59. package/templates/test-include.html +11 -0
@@ -0,0 +1,224 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>注册页</title>
8
+
9
+ <!-- 引入公共主题变量与全局样式 -->
10
+ <link rel="stylesheet" href="/static/constants.css" /> <!-- 样式常量 -->
11
+ <link rel="stylesheet" href="/static/public.css" /> <!-- 公共样式 -->
12
+ <link rel="stylesheet" href="/static/themeImg.css" /> <!-- 主题图标 -->
13
+ <link rel="stylesheet" href="/static/topImg.css" /> <!-- 返回顶部图标 -->
14
+ <style>
15
+ /* 覆盖 body 布局为居中卡片 */
16
+ body {
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ padding: 20px;
21
+ background: var(--body-bg);
22
+ background-color: var(--bg-color);
23
+ min-height: 100vh;
24
+ }
25
+
26
+ /* 卡片容器 */
27
+ .card {
28
+ background: var(--container-bg);
29
+ border-radius: 12px;
30
+ box-shadow: 0 20px 40px var(--content-shadow);
31
+ width: 100%;
32
+ max-width: 400px;
33
+ padding: 40px 30px;
34
+ transition: background 0.5s ease, box-shadow 0.5s ease;
35
+ }
36
+
37
+ .card h2 {
38
+ color: var(--h1-color);
39
+ margin-bottom: 30px;
40
+ text-align: center;
41
+ font-weight: 600;
42
+ font-size: 28px;
43
+ border-bottom: none;
44
+ }
45
+
46
+ .form-group {
47
+ margin-bottom: 20px;
48
+ }
49
+
50
+ .form-group label {
51
+ display: block;
52
+ margin-bottom: 8px;
53
+ color: var(--text-color);
54
+ font-weight: 500;
55
+ font-size: 14px;
56
+ opacity: 0.85;
57
+ }
58
+
59
+ .form-group input {
60
+ width: 100%;
61
+ padding: 12px 16px;
62
+ border: 1px solid var(--content-border);
63
+ border-radius: 8px;
64
+ font-size: 16px;
65
+ background: var(--li-bg);
66
+ color: var(--text-color);
67
+ transition: border-color 0.2s, background 0.5s ease, color 0.5s ease;
68
+ }
69
+
70
+ .form-group input:focus {
71
+ border-color: var(--link-color);
72
+ outline: none;
73
+ }
74
+
75
+ .btn {
76
+ width: 100%;
77
+ padding: 14px;
78
+ background: var(--btn-bg);
79
+ color: var(--text-color);
80
+ border: none;
81
+ border-radius: 8px;
82
+ font-size: 16px;
83
+ font-weight: 600;
84
+ cursor: pointer;
85
+ box-shadow: 0 4px 12px var(--content-shadow);
86
+ transition: background 0.3s ease, transform 0.2s ease;
87
+ }
88
+
89
+ .btn:hover {
90
+ background: var(--btn-hover);
91
+ transform: translateY(-2px);
92
+ }
93
+
94
+ .btn:disabled {
95
+ background: #b0b0b0;
96
+ cursor: not-allowed;
97
+ opacity: 0.65;
98
+ transform: none;
99
+ }
100
+
101
+ .error {
102
+ color: #e53e3e;
103
+ font-size: 14px;
104
+ margin-top: 10px;
105
+ text-align: center;
106
+ }
107
+
108
+ .success {
109
+ color: #38a169;
110
+ font-size: 14px;
111
+ margin-top: 10px;
112
+ text-align: center;
113
+ }
114
+
115
+ .links {
116
+ margin-top: 25px;
117
+ text-align: center;
118
+ font-size: 14px;
119
+ color: var(--text-color);
120
+ }
121
+
122
+ .links a {
123
+ color: var(--link-color);
124
+ text-decoration: none;
125
+ transition: color 0.2s;
126
+ }
127
+
128
+ .links a:hover {
129
+ color: var(--link-hover);
130
+ text-decoration: underline;
131
+ }
132
+
133
+ .hidden {
134
+ display: none;
135
+ }
136
+ </style>
137
+ </head>
138
+
139
+ <body>
140
+ <div class="card">
141
+ <h2>注册</h2>
142
+ <form onsubmit="return false;">
143
+ <div class="form-group">
144
+ <label for="username">用户名</label>
145
+ <input type="text" id="username" placeholder="请输入用户名" maxlength="50" autofocus autocomplete="username">
146
+ </div>
147
+ <div class="form-group">
148
+ <label for="email">邮箱</label>
149
+ <input type="email" id="email" placeholder="请输入邮箱" maxlength="100" autocomplete="email">
150
+ </div>
151
+ <div class="form-group">
152
+ <label for="password">密码 (至少6位)</label>
153
+ <input type="password" id="password" placeholder="请输入密码" maxlength="72" autocomplete="current-password">
154
+ </div>
155
+ <button class="btn" id="registerBtn">注册</button>
156
+ </form>
157
+ <div id="message" class="error"></div>
158
+ <div id="manualLink" class="links hidden">
159
+ <a href="/login">已有账号?立即登录</a>
160
+ </div>
161
+ </div>
162
+
163
+ <!-- 公共逻辑 -->
164
+ <script src="/static/themeModule.js" defer></script><!-- 引入主题自适应模块 -->
165
+ <script src="/static/mouseOrTouch.js" defer></script><!-- 引入鼠标或触摸操作 -->
166
+ <script src="/static/themeImg.js" defer></script> <!-- 引入主题图标模块 -->
167
+ <script src="/static/topImg.js" defer></script> <!-- 引入返回顶部图标模块 -->
168
+ <script>
169
+ let pollInterval = null, registeredEmail = null;
170
+ const [usernameEl, emailEl, passwordEl, messageDiv, manualLink, registerBtn] = ['username', 'email', 'password',
171
+ 'message', 'manualLink', 'registerBtn'].map(id => document.getElementById(id)),
172
+ handleError = msg => {
173
+ messageDiv.className = 'error', messageDiv.textContent = msg, registerBtn.disabled = false;
174
+ },
175
+ stopPolling = () => {
176
+ clearInterval(pollInterval), pollInterval = null;
177
+ },
178
+ startVerificationPolling = email => {
179
+ registeredEmail = email, stopPolling();
180
+ pollInterval = setInterval(async () => {
181
+ try {
182
+ const res = await fetch(`/api/check-email-verified?email=${encodeURIComponent(email)}`),
183
+ data = await res.json();
184
+ if (res.ok && data.verified) stopPolling(), window.location.href = '/login';
185
+ else console.error('检查验证状态失败');
186
+ } catch (err) {
187
+ console.error('轮询检查邮箱验证状态失败', err);
188
+ }
189
+ }, 2000);
190
+ setTimeout(() => {
191
+ if (pollInterval) stopPolling(), console.log('邮箱验证轮询超时');
192
+ }, 900000);
193
+ };
194
+
195
+ registerBtn.addEventListener('click', async () => {
196
+ registerBtn.disabled = true, messageDiv.textContent = '', manualLink.classList.add('hidden');
197
+ const [username, email, password] = [usernameEl, emailEl, passwordEl].map(input => input.value.trim());
198
+ if (!username || !email || !password) return handleError('请填写所有字段');
199
+ if (password.length < 6) return handleError('密码至少6位');
200
+
201
+ try {
202
+ const response = await fetch('/api/register', {
203
+ method: 'POST',
204
+ headers: { 'Content-Type': 'application/json' },
205
+ body: JSON.stringify({ username, email, password })
206
+ }), data = await response.json();
207
+
208
+ if (response.ok) {
209
+ messageDiv.className = 'success', messageDiv.textContent = data.message;
210
+ manualLink.classList.remove('hidden'), startVerificationPolling(email);
211
+ }
212
+ else handleError(data.message);
213
+ } catch (err) { handleError('网络错误,请稍后重试'); }
214
+ });
215
+
216
+ passwordEl.addEventListener('keypress', (e) => {
217
+ if (e.key === 'Enter') registerBtn.click();
218
+ });
219
+ [usernameEl, emailEl, passwordEl].forEach(el => el.addEventListener('input', () => messageDiv.textContent = ''));
220
+ window.addEventListener('beforeunload', stopPolling);
221
+ </script>
222
+ </body>
223
+
224
+ </html>
@@ -0,0 +1,205 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>重置密码页</title>
8
+
9
+ <!-- 引入公共主题变量与全局样式 -->
10
+ <link rel="stylesheet" href="/static/constants.css" /> <!-- 样式常量 -->
11
+ <link rel="stylesheet" href="/static/public.css" /> <!-- 公共样式 -->
12
+ <link rel="stylesheet" href="/static/themeImg.css" /> <!-- 主题图标 -->
13
+ <link rel="stylesheet" href="/static/topImg.css" /> <!-- 返回顶部图标 -->
14
+
15
+ <style>
16
+ /* body 居中布局覆盖 */
17
+ body {
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ padding: 20px;
22
+ background: var(--body-bg);
23
+ background-color: var(--bg-color);
24
+ min-height: 100vh;
25
+ }
26
+
27
+ /* 卡片容器 */
28
+ .card {
29
+ background: var(--container-bg);
30
+ border-radius: 12px;
31
+ box-shadow: 0 20px 40px var(--content-shadow);
32
+ width: 80%;
33
+ min-width: 400px;
34
+ padding: 40px 30px;
35
+ transition: all 0.3s ease;
36
+ }
37
+
38
+ .card h2 {
39
+ color: var(--h1-color);
40
+ margin-bottom: 30px;
41
+ text-align: center;
42
+ font-weight: 600;
43
+ font-size: 28px;
44
+ border-bottom: none;
45
+ }
46
+
47
+ .form-group {
48
+ margin-bottom: 20px;
49
+ }
50
+
51
+ .form-group input {
52
+ width: 100%;
53
+ padding: 12px 16px;
54
+ border: 1px solid var(--content-border);
55
+ border-radius: 8px;
56
+ font-size: 16px;
57
+ background: var(--li-bg);
58
+ color: var(--text-color);
59
+ transition: border-color 0.2s, background 0.5s ease, color 0.5s ease;
60
+ }
61
+
62
+ .form-group input:focus {
63
+ outline: none;
64
+ border-color: var(--link-color);
65
+ }
66
+
67
+ .btn {
68
+ width: 100%;
69
+ padding: 14px;
70
+ background: var(--btn-bg);
71
+ color: var(--text-color);
72
+ border: none;
73
+ border-radius: 8px;
74
+ font-size: 16px;
75
+ font-weight: 600;
76
+ cursor: pointer;
77
+ box-shadow: 0 4px 12px var(--content-shadow);
78
+ transition: background 0.3s ease, transform 0.2s ease;
79
+ }
80
+
81
+ .btn:hover {
82
+ background: var(--btn-hover);
83
+ transform: translateY(-2px);
84
+ }
85
+
86
+ .btn:disabled {
87
+ background: #b0b0b0;
88
+ cursor: not-allowed;
89
+ opacity: 0.65;
90
+ transform: none;
91
+ }
92
+
93
+ .error,
94
+ .success {
95
+ font-size: 14px;
96
+ margin-top: 10px;
97
+ text-align: center;
98
+ }
99
+
100
+ .error {
101
+ color: #e53e3e;
102
+ }
103
+
104
+ .success {
105
+ color: #38a169;
106
+ }
107
+
108
+ .big-success {
109
+ font-size: 32px;
110
+ font-weight: 600;
111
+ margin: 40px 0;
112
+ line-height: 1.4;
113
+ color: var(--h2-color);
114
+ }
115
+
116
+ .links {
117
+ margin-top: 25px;
118
+ text-align: center;
119
+ font-size: 14px;
120
+ color: var(--text-color);
121
+ }
122
+
123
+ .links a {
124
+ color: var(--link-color);
125
+ text-decoration: none;
126
+ transition: color 0.2s;
127
+ }
128
+
129
+ .links a:hover {
130
+ color: var(--link-hover);
131
+ text-decoration: underline;
132
+ }
133
+
134
+ .hidden {
135
+ display: none;
136
+ }
137
+ </style>
138
+ </head>
139
+
140
+ <body>
141
+ <div class="card">
142
+ <h2 id="pageTitle">重置密码</h2>
143
+ <form onsubmit="return false;">
144
+ <div class="form-group" id="newPasswordGroup">
145
+ <input type="password" id="newPassword" placeholder="新密码 (至少6位)" maxlength="72" autofocus
146
+ autocomplete="new-password">
147
+ </div>
148
+ <div class="form-group" id="confirmPasswordGroup">
149
+ <input type="password" id="confirmPassword" placeholder="确认新密码" maxlength="72"
150
+ autocomplete="new-password">
151
+ </div>
152
+ <button class="btn" id="resetBtn">重置密码</button>
153
+ </form>
154
+ <div id="message" class="error"></div>
155
+ </div>
156
+
157
+ <!-- 公共逻辑 -->
158
+ <script src="/static/themeModule.js" defer></script><!-- 引入主题自适应模块 -->
159
+ <script src="/static/mouseOrTouch.js" defer></script><!-- 引入鼠标或触摸操作 -->
160
+ <script src="/static/themeImg.js" defer></script> <!-- 引入主题图标模块 -->
161
+ <script src="/static/topImg.js" defer></script> <!-- 引入返回顶部图标模块 -->
162
+ <script>
163
+ const urlParams = new URLSearchParams(window.location.search), token = urlParams.get('token'), [messageDiv, resetBtn,
164
+ title, newPwdEl, newPwdGroup, confirmPwdEl, confirmPwdGroup] = ['message', 'resetBtn', 'pageTitle', 'newPassword',
165
+ 'newPasswordGroup', 'confirmPassword', 'confirmPasswordGroup'].map(id => document.getElementById(id)),
166
+ handleError = msg => {
167
+ messageDiv.className = 'error', messageDiv.textContent = msg, resetBtn.disabled = false;
168
+ };
169
+
170
+ if (!token) messageDiv.className = 'error', messageDiv.textContent = '无效的链接,请返回首页重试', resetBtn.disabled = true;
171
+
172
+ resetBtn.addEventListener('click', async () => {
173
+ resetBtn.disabled = true, messageDiv.textContent = '';
174
+ const [newPwd, confirmPwd] = [newPwdEl, confirmPwdEl].map(el => el.value.trim());
175
+
176
+ if (!newPwd || !confirmPwd) return handleError('请填写密码');
177
+ if (newPwd.length < 6) return handleError('密码至少6位');
178
+ if (newPwd !== confirmPwd) return handleError('两次密码不一致');
179
+
180
+ try {
181
+ const response = await fetch('/api/reset-password', {
182
+ method: 'POST',
183
+ headers: { 'Content-Type': 'application/json' },
184
+ body: JSON.stringify({ token, newPwd })
185
+ }), data = await response.json();
186
+
187
+ if (response.ok) {
188
+ [title, newPwdGroup, confirmPwdGroup, resetBtn].forEach(el => el.classList.add('hidden'));
189
+ messageDiv.className = 'success big-success', messageDiv.textContent = data.message;
190
+
191
+ let seconds = 3;
192
+ const timer = setInterval(() => {
193
+ seconds--;
194
+ if (seconds <= 0) clearInterval(timer), window.close(), messageDiv.textContent = '密码已重置,请手动关闭页面!';
195
+ else messageDiv.textContent = `密码已重置\n${seconds}秒后自动关闭...`;
196
+ }, 1000);
197
+ }
198
+ else handleError(data.message);
199
+ } catch (err) { handleError('网络错误,请稍后重试') }
200
+ });
201
+ [newPwdEl, confirmPwdEl].forEach(el => el.addEventListener('input', () => messageDiv.textContent = ''));
202
+ </script>
203
+ </body>
204
+
205
+ </html>
@@ -0,0 +1,163 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>邮箱验证页</title>
8
+
9
+ <!-- 引入公共主题变量与全局样式 -->
10
+ <link rel="stylesheet" href="/static/constants.css" /> <!-- 样式常量 -->
11
+ <link rel="stylesheet" href="/static/public.css" /> <!-- 公共样式 -->
12
+ <link rel="stylesheet" href="/static/themeImg.css" /> <!-- 主题图标 -->
13
+ <link rel="stylesheet" href="/static/topImg.css" /> <!-- 返回顶部图标 -->
14
+ <style>
15
+ /* 覆盖 body 布局:居中卡片,背景由变量接管 */
16
+ body {
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ padding: 20px;
21
+ background: var(--body-bg);
22
+ background-color: var(--bg-color);
23
+ }
24
+
25
+ /* 卡片容器 */
26
+ .card {
27
+ background: var(--container-bg);
28
+ border-radius: 12px;
29
+ box-shadow: 0 20px 40px var(--content-shadow);
30
+ width: 80%;
31
+ min-width: 400px;
32
+ padding: 40px 30px;
33
+ text-align: center;
34
+ transition: background 0.5s ease, box-shadow 0.5s ease;
35
+ }
36
+
37
+ .card h2 {
38
+ color: var(--h2-color);
39
+ margin-bottom: 20px;
40
+ font-weight: 600;
41
+ font-size: 28px;
42
+ }
43
+
44
+ .message {
45
+ font-size: 16px;
46
+ margin: 20px 0;
47
+ color: var(--p-color);
48
+ }
49
+
50
+ .success {
51
+ color: #38a169;
52
+ }
53
+
54
+ .error {
55
+ color: #e53e3e;
56
+ }
57
+
58
+ .big-success {
59
+ font-size: 32px;
60
+ font-weight: 600;
61
+ margin: 40px 0;
62
+ line-height: 1.4;
63
+ white-space: pre-line;
64
+ color: #38a169;
65
+ }
66
+
67
+ .btn {
68
+ display: inline-block;
69
+ padding: 12px 24px;
70
+ background: var(--btn-bg);
71
+ color: var(--text-color);
72
+ border: none;
73
+ border-radius: 8px;
74
+ font-size: 16px;
75
+ font-weight: 600;
76
+ text-decoration: none;
77
+ cursor: pointer;
78
+ transition: background 0.2s;
79
+ margin-top: 20px;
80
+ box-shadow: 0 4px 12px var(--content-shadow);
81
+ }
82
+
83
+ .btn:hover {
84
+ background: var(--btn-hover);
85
+ transform: translateY(-2px);
86
+ }
87
+
88
+ .link {
89
+ margin-top: 15px;
90
+ font-size: 14px;
91
+ color: var(--text-color);
92
+ }
93
+
94
+ .link a {
95
+ color: var(--link-color);
96
+ text-decoration: none;
97
+ }
98
+
99
+ .link a:hover {
100
+ color: var(--link-hover);
101
+ text-decoration: underline;
102
+ }
103
+
104
+ .hidden {
105
+ display: none;
106
+ }
107
+ </style>
108
+ </head>
109
+
110
+ <body>
111
+ <div class="card">
112
+ <h2 id="pageTitle">邮箱验证</h2>
113
+ <div id="message" class="message">正在验证,请稍候...</div>
114
+ <div id="action" style="display:none;"></div>
115
+ </div>
116
+
117
+ <!-- 公共逻辑 -->
118
+ <script src="/static/themeModule.js" defer></script><!-- 引入主题自适应模块 -->
119
+ <script src="/static/mouseOrTouch.js" defer></script><!-- 引入鼠标或触摸操作 -->
120
+ <script src="/static/themeImg.js" defer></script> <!-- 引入主题图标模块 -->
121
+ <script src="/static/topImg.js" defer></script> <!-- 引入返回顶部图标模块 -->
122
+ <script>
123
+ (async () => {
124
+ const urlParams = new URLSearchParams(window.location.search), token = urlParams.get('token'),
125
+ type = urlParams.get('type') || 'register', [messageDiv, actionDiv, title] = ['message', 'action', 'pageTitle']
126
+ .map(id => document.getElementById(id)),
127
+
128
+ // 根据 type 决定失败时的操作链接
129
+ getFallbackLink = () => {
130
+ if (type === 'new-email') return '<a href="/profile">返回个人资料页</a>';
131
+ else return '<a href="/register">返回注册页</a>';
132
+ },
133
+ handleError = msg => {
134
+ messageDiv.className = 'error', messageDiv.textContent = msg;
135
+ actionDiv.innerHTML = `<div class="link">${getFallbackLink()}</div>`, actionDiv.style.display = 'block';
136
+ };
137
+
138
+ if (!token) return handleError('无效的验证链接');
139
+
140
+ // 根据 type 选择不同的 API 端点
141
+ const apiEndpoint = type === 'new-email' ? '/api/verify-new-email' : '/api/verify-email';
142
+ try {
143
+ const response = await fetch(`${apiEndpoint}?token=${encodeURIComponent(token)}`), data = await response.json();
144
+
145
+ if (response.ok) {
146
+ title.classList.add('hidden'), actionDiv.style.display = 'none';
147
+ messageDiv.className = 'success big-success', messageDiv.textContent = data.message;
148
+
149
+ let seconds = 3;
150
+ const timer = setInterval(() => {
151
+ seconds--;
152
+ if (seconds <= 0)
153
+ clearInterval(timer), window.close(), messageDiv.textContent = '验证成功,请手动关闭页面;';
154
+ else messageDiv.textContent = `验证成功\n${seconds}秒后自动关闭...`;
155
+ }, 1000);
156
+ }
157
+ else handleError(data.message);
158
+ } catch (err) { handleError('网络错误,请稍后重试'); }
159
+ })();
160
+ </script>
161
+ </body>
162
+
163
+ </html>
@@ -0,0 +1,71 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <!--网站关键字-->
8
+ <meta name="keywords" content=[!keywords]""[~keywords] />
9
+ <!--网站描述-->
10
+ <meta name="description" content=[!description]""[~description] />
11
+ <!--页面定时跳转 值([秒],地址)-->
12
+ <meta http-equiv="refresh" content=[!refresh]""[~refresh] />
13
+ <!--标题栏显示图标-->
14
+ <link rel=" icon" type="image/x-icon" href=[!favicon]"/static/img/favicon.ico"[~favicon] />
15
+ <!--收藏栏显示图标-->
16
+ <link rel="shortcut icon" type="image/x-icon" href=[!shortcutIcon]"/static/img/favicon.ico"[~shortcutIcon] />
17
+ <!-- 网站标题 -->
18
+ <title>[!title][~title]</title>
19
+ <link rel="stylesheet" href="/static/constants.css" /> <!-- 样式常量 -->
20
+ <link rel="stylesheet" href="/static/public.css" /> <!-- 公共样式 -->
21
+ <link rel="stylesheet" href="/static/themeImg.css" /> <!-- 主题图标 -->
22
+ <link rel="stylesheet" href="/static/topImg.css" /> <!-- 返回顶部图标 -->
23
+ [!style]<!-- 样式区域 -->[~style]
24
+ </head>
25
+
26
+ <body onload="document.body.classList.add('page-ready')">
27
+
28
+ <header>
29
+ [!header]<a href="index.html" class="home-link">← 返回首页</a>[~header]
30
+
31
+ [!my]
32
+ <!-- 个人中心按钮 注意: 此按钮仅在用户启用登录并成功登录后显示 -->
33
+ <div id="userCenter" class="user-center" style="display: none;">
34
+ <a href="/profile">👤 个人中心</a>
35
+ </div>
36
+ [~my]
37
+ </header>
38
+ <div class="container">
39
+ <!-- 默认内容区域 -->
40
+ [!content]
41
+ [!test][~test]<!-- 嵌套标签可以写,但会被忽略为普通标签,最终会被清理 -->
42
+ [~content]
43
+ </div>
44
+
45
+ [!variables]
46
+ <!-- 变量替换测试 -->
47
+ <div style="background: #459064; padding: 15px; margin: 20px 0; border-radius: 5px;">
48
+ <h3>模板功能测试 - 变量替换</h3>
49
+ <p>当前年份: {{year}}</p>
50
+ <p>时间戳: {{timestamp}}</p>
51
+ <p>基础URL: {{baseUrl}}</p>
52
+ </div>
53
+ [~variables]
54
+
55
+ <footer>
56
+ <!-- 脚本 -->
57
+ [!footer][~footer]
58
+ </footer>
59
+
60
+ <!-- 公共逻辑 -->
61
+ <script src="/static/auth.js" defer></script><!-- 登录状态检测 -->
62
+ <script src="/static/themeModule.js" defer></script><!-- 引入主题自适应模块 -->
63
+ <script src="/static/mouseOrTouch.js" defer></script><!-- 引入鼠标或触摸操作 -->
64
+ <script src="/static/themeImg.js" defer></script> <!-- 引入主题图标模块 -->
65
+ <script src="/static/topImg.js" defer></script> <!-- 引入返回顶部图标模块 -->
66
+ [!script]
67
+
68
+ [~script]
69
+ </body>
70
+
71
+ </html>
@@ -0,0 +1,5 @@
1
+ <div style="border-top: 2px solid #c84b4b; padding-top: 10px; margin-top: 20px; font-size: 0.9em; color: #666;">
2
+ <p>这是通过 <code>"[include footer-content.html]"</code> 包含的内容</p>
3
+ <p>包含时间: {{timestamp}} ({{year}}年)</p>
4
+ <p>基础URL: {{baseUrl}}</p>
5
+ </div>