podflow 20250417.2__py3-none-any.whl → 20250429__py3-none-any.whl
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.
- podflow/__init__.py +1 -0
- podflow/download/dl_aideo_video.py +12 -0
- podflow/download/show_progress.py +22 -5
- podflow/download/youtube_and_bilibili_download.py +3 -2
- podflow/httpfs/app_bottle.py +74 -13
- podflow/httpfs/download_bar.py +38 -0
- podflow/main_podcast.py +10 -5
- podflow/templates/css/index.css +398 -0
- podflow/templates/index.html +10 -654
- podflow/templates/js/index.js +725 -0
- podflow/templates/js/qrcode.min.js +1 -0
- {podflow-20250417.2.dist-info → podflow-20250429.dist-info}/METADATA +1 -1
- {podflow-20250417.2.dist-info → podflow-20250429.dist-info}/RECORD +16 -12
- {podflow-20250417.2.dist-info → podflow-20250429.dist-info}/WHEEL +0 -0
- {podflow-20250417.2.dist-info → podflow-20250429.dist-info}/entry_points.txt +0 -0
- {podflow-20250417.2.dist-info → podflow-20250429.dist-info}/top_level.txt +0 -0
podflow/templates/index.html
CHANGED
@@ -4,333 +4,8 @@
|
|
4
4
|
<meta charset="UTF-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
6
|
<title>Podflow</title>
|
7
|
-
<
|
8
|
-
|
9
|
-
:root {
|
10
|
-
--bg-color: #f8f9fa;
|
11
|
-
--text-color: #333333;
|
12
|
-
--secondary-text: #666666;
|
13
|
-
--input-bg: #ffffff;
|
14
|
-
--input-border: #cccccc;
|
15
|
-
--button-bg: #007bff;
|
16
|
-
--button-hover: #0056b3;
|
17
|
-
--button-text: #ffffff;
|
18
|
-
--button-shadow: hsla(0, 0%, 0%, 0.20);
|
19
|
-
--menu-bg: #f0f0f0;
|
20
|
-
--menu-text: #333333;
|
21
|
-
--menu-width: 170px;
|
22
|
-
--menu-selected-bg: #cccccc;
|
23
|
-
/* 将进度条颜色变量修改为蓝色 */
|
24
|
-
--progress-bar-color: #0d6efd; /* 标准蓝色 */
|
25
|
-
--background-bar-color: #e0e0e0; /* 浅色背景下的进度条颜色 */
|
26
|
-
}
|
27
|
-
|
28
|
-
/* 深色模式变量 */
|
29
|
-
@media (prefers-color-scheme: dark) {
|
30
|
-
:root {
|
31
|
-
--bg-color: #222222;
|
32
|
-
--text-color: #e0e0e0;
|
33
|
-
--secondary-text: #aaaaaa;
|
34
|
-
--input-bg: #333333;
|
35
|
-
--input-border: #555555;
|
36
|
-
--button-bg: #0062cc;
|
37
|
-
--button-hover: #0069d9;
|
38
|
-
--button-text: #f0f0f0;
|
39
|
-
--button-shadow: hsla(0, 0%, 0%, 0.40);
|
40
|
-
--menu-bg: #333333;
|
41
|
-
--menu-text: #e0e0e0;
|
42
|
-
--menu-selected-bg: #555555;
|
43
|
-
/* 将深色模式下的进度条颜色修改为较亮的蓝色 */
|
44
|
-
--progress-bar-color: #315188; /* 较亮的蓝色,适合深色背景 */
|
45
|
-
--background-bar-color: #333333; /* 深色背景下的进度条颜色 */
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
.ansi-url{
|
50
|
-
color: #4064a0;
|
51
|
-
text-decoration: underline;
|
52
|
-
}
|
53
|
-
|
54
|
-
/* ... (其余的 ANSI 颜色、基本样式、表单样式、按钮样式、提示样式、菜单切换按钮、手机端优化、消息和二维码样式保持不变) ... */
|
55
|
-
.ansi-30m { color: #000000; }
|
56
|
-
.ansi-31m { color: #bb271b; }
|
57
|
-
.ansi-32m { color: #61992e; }
|
58
|
-
.ansi-33m { color: #bea235; }
|
59
|
-
.ansi-34m { color: #4064a0; }
|
60
|
-
.ansi-35m { color: #705278; }
|
61
|
-
.ansi-36m { color: #449598; }
|
62
|
-
.ansi-37m { color: #d4d7d0; }
|
63
|
-
.ansi-90m { color: #565752; }
|
64
|
-
.ansi-91m { color: #dc3f36; }
|
65
|
-
.ansi-92m { color: #53b739; }
|
66
|
-
.ansi-93m { color: #f9ea6b; }
|
67
|
-
.ansi-94m { color: #7c9ecb; }
|
68
|
-
.ansi-95m { color: #de30c5; }
|
69
|
-
.ansi-96m { color: #51b2bb; }
|
70
|
-
.ansi-97m { color: #eeeeec; }
|
71
|
-
|
72
|
-
@media (prefers-color-scheme: dark) {
|
73
|
-
.ansi-30m { color: #ffffff; }
|
74
|
-
.ansi-31m { color: #bb271b; }
|
75
|
-
.ansi-32m { color: #61992e; }
|
76
|
-
.ansi-33m { color: #bea235; }
|
77
|
-
.ansi-34m { color: #4064a0; }
|
78
|
-
.ansi-35m { color: #705278; }
|
79
|
-
.ansi-36m { color: #449598; }
|
80
|
-
.ansi-37m { color: #d4d7d0; }
|
81
|
-
.ansi-90m { color: #565752; }
|
82
|
-
.ansi-91m { color: #dc3f36; }
|
83
|
-
.ansi-92m { color: #53b739; }
|
84
|
-
.ansi-93m { color: #f9ea6b; }
|
85
|
-
.ansi-94m { color: #7c9ecb; }
|
86
|
-
.ansi-95m { color: #de30c5; }
|
87
|
-
.ansi-96m { color: #51b2bb; }
|
88
|
-
.ansi-97m { color: #eeeeec; }
|
89
|
-
}
|
90
|
-
|
91
|
-
/* 基本样式 */
|
92
|
-
body {
|
93
|
-
font-family: Arial, sans-serif;
|
94
|
-
background-color: var(--bg-color);
|
95
|
-
color: var(--text-color);
|
96
|
-
transition: background-color 0.3s, color 0.3s;
|
97
|
-
margin: 0;
|
98
|
-
display: flex;
|
99
|
-
}
|
100
|
-
nav {
|
101
|
-
width: var(--menu-width);
|
102
|
-
background-color: var(--menu-bg);
|
103
|
-
color: var(--menu-text);
|
104
|
-
height: 100vh;
|
105
|
-
position: sticky;
|
106
|
-
top: 0;
|
107
|
-
z-index: 1000;
|
108
|
-
transition: width 0.3s, padding 0.3s, color 0.3s;
|
109
|
-
}
|
110
|
-
nav.hidden {
|
111
|
-
width: 0;
|
112
|
-
padding: 0;
|
113
|
-
overflow: hidden;
|
114
|
-
}
|
115
|
-
nav ul {
|
116
|
-
list-style: none;
|
117
|
-
padding: 0;
|
118
|
-
}
|
119
|
-
nav h3 {
|
120
|
-
text-align: center;
|
121
|
-
margin: 20px 0 0;
|
122
|
-
font-size: 20px;
|
123
|
-
}
|
124
|
-
nav li {
|
125
|
-
margin: 5px 0;
|
126
|
-
cursor: pointer;
|
127
|
-
transition: background-color 0.3s, color 0.3s;
|
128
|
-
padding: 0 20px;
|
129
|
-
height: 40px;
|
130
|
-
line-height: 40px;
|
131
|
-
}
|
132
|
-
nav li:hover {
|
133
|
-
background-color: var(--menu-selected-bg);
|
134
|
-
color: var(--button-hover);
|
135
|
-
}
|
136
|
-
main {
|
137
|
-
flex: 1;
|
138
|
-
padding: 40px;
|
139
|
-
max-width: 520px;
|
140
|
-
transition: margin-left 0.3s;
|
141
|
-
}
|
142
|
-
main.full {
|
143
|
-
margin-left: 0;
|
144
|
-
}
|
145
|
-
|
146
|
-
/* 表单与消息区域共用样式 */
|
147
|
-
.common-area {
|
148
|
-
width: 100%;
|
149
|
-
max-width: 520px;
|
150
|
-
padding: 2px;
|
151
|
-
color: var(--text-color);
|
152
|
-
transition: background-color 0.3s, color 0.3s, border-color 0.3s;
|
153
|
-
box-sizing: border-box;
|
154
|
-
line-height: 1.1;
|
155
|
-
overflow-x: auto; /* 当内容超出宽度时显示水平滚动条(可选) */
|
156
|
-
overflow-y: auto;
|
157
|
-
font-family: 'Courier New', Courier, monospace; /* 添加字体 */
|
158
|
-
word-break: break-word;
|
159
|
-
overflow-wrap: break-word;
|
160
|
-
white-space: pre-wrap; /* 保留换行并允许自动换行 */
|
161
|
-
}
|
162
|
-
textarea {
|
163
|
-
height: 250px;
|
164
|
-
font-size: 16px;
|
165
|
-
border: 1px solid var(--input-border);
|
166
|
-
border-radius: 4px;
|
167
|
-
background-color: var(--input-bg);
|
168
|
-
}
|
169
|
-
#messageDownload {
|
170
|
-
max-height: 200px;
|
171
|
-
height: auto;
|
172
|
-
font-size: 12px;
|
173
|
-
border: 0px;
|
174
|
-
border-radius: 4px;
|
175
|
-
background-color: var(--bg-color);
|
176
|
-
}
|
177
|
-
#messageArea {
|
178
|
-
height: 250px;
|
179
|
-
font-size: 12px;
|
180
|
-
border: 1px solid var(--input-border);
|
181
|
-
border-radius: 4px;
|
182
|
-
background-color: var(--input-bg);
|
183
|
-
}
|
184
|
-
#messageHttp {
|
185
|
-
height: 100px;
|
186
|
-
font-size: 12px;
|
187
|
-
border: 1px solid var(--input-border);
|
188
|
-
border-radius: 4px;
|
189
|
-
background-color: var(--input-bg);
|
190
|
-
}
|
191
|
-
.button-container {
|
192
|
-
margin-top: 10px;
|
193
|
-
display: flex;
|
194
|
-
justify-content: center; /* 水平居中 */
|
195
|
-
flex-wrap: wrap; /* 换行 */
|
196
|
-
gap: 10px; /* 按钮间距 */
|
197
|
-
}
|
198
|
-
button {
|
199
|
-
background-color: var(--button-bg);
|
200
|
-
color: var(--button-text);
|
201
|
-
border: none;
|
202
|
-
padding: 12px 18px;
|
203
|
-
font-size: 16px;
|
204
|
-
border-radius: 6px;
|
205
|
-
cursor: pointer;
|
206
|
-
transition: background-color 0.3s, box-shadow 0.3s;
|
207
|
-
box-shadow: 2px 2px 8px var(--button-shadow);
|
208
|
-
margin: 5px;
|
209
|
-
}
|
210
|
-
button:hover {
|
211
|
-
background-color: var(--button-hover);
|
212
|
-
}
|
213
|
-
.hint {
|
214
|
-
font-size: 14px;
|
215
|
-
color: var(--secondary-text);
|
216
|
-
margin-top: 10px;
|
217
|
-
}
|
218
|
-
/* 菜单切换按钮 */
|
219
|
-
#toggleMenu {
|
220
|
-
width: 35px;
|
221
|
-
height: 40px;
|
222
|
-
position: fixed;
|
223
|
-
left: var(--menu-width);
|
224
|
-
top: 5px;
|
225
|
-
background: var(--menu-bg);
|
226
|
-
border: none;
|
227
|
-
font-size: 20px;
|
228
|
-
color: var(--text-color);
|
229
|
-
cursor: pointer;
|
230
|
-
transition: left 0.3s, color 0.3s;
|
231
|
-
border-radius: 0 10px 10px 0;
|
232
|
-
display: flex;
|
233
|
-
justify-content: center;
|
234
|
-
align-items: center;
|
235
|
-
box-shadow: 0px 0px 8px var(--button-shadow);
|
236
|
-
margin: 0;
|
237
|
-
}
|
238
|
-
#toggleMenu:hover {
|
239
|
-
color: var(--button-hover);
|
240
|
-
}
|
241
|
-
|
242
|
-
/* 手机端优化 */
|
243
|
-
@media (max-width: 600px) {
|
244
|
-
#messageArea, textarea {
|
245
|
-
max-width: none;
|
246
|
-
}
|
247
|
-
button {
|
248
|
-
width: 90%;
|
249
|
-
}
|
250
|
-
nav {
|
251
|
-
position: fixed;
|
252
|
-
}
|
253
|
-
/* 确保页面宽度不会超出视口 */
|
254
|
-
body, html {
|
255
|
-
width: 100%;
|
256
|
-
overflow-x: hidden;
|
257
|
-
}
|
258
|
-
}
|
259
|
-
.message {
|
260
|
-
padding: 0px;
|
261
|
-
margin: 0px;
|
262
|
-
}
|
263
|
-
|
264
|
-
/* 二维码容器样式 */
|
265
|
-
.qrcode-container {
|
266
|
-
display: inline-block; /* 让容器并排显示 */
|
267
|
-
margin: 0px;
|
268
|
-
}
|
269
|
-
|
270
|
-
/* 主进度条样式 */
|
271
|
-
.progress-bar {
|
272
|
-
width: 100%;
|
273
|
-
height: 22px;
|
274
|
-
background-color: var(--background-bar-color); /* 考虑使用变量 */
|
275
|
-
border-radius: 12px;
|
276
|
-
overflow: hidden;
|
277
|
-
position: relative;
|
278
|
-
border: 1px solid var(--input-border);
|
279
|
-
}
|
280
|
-
.progress-bar .progress {
|
281
|
-
height: 100%;
|
282
|
-
background-color: var(--progress-bar-color);
|
283
|
-
width: 0%; /* 根据实际进度更新 */
|
284
|
-
transition: width 0.3s ease-in-out;
|
285
|
-
border-radius: 12px;
|
286
|
-
/* 多背景 */
|
287
|
-
background-image:
|
288
|
-
linear-gradient(to right, transparent 0%, rgba(255, 255, 255, 0.5) 50%, transparent 100%),
|
289
|
-
linear-gradient(to right, rgba(255, 255, 255, 0.15) 0%, rgba(0, 0, 0, 0.15) 100%);
|
290
|
-
background-size: 200% 100%, 100% 100%;
|
291
|
-
background-repeat: no-repeat, no-repeat;
|
292
|
-
/* 初始背景位置 */
|
293
|
-
background-position: 100% 0, 0 0; /* 脉冲初始在左侧 */
|
294
|
-
}
|
295
|
-
/* 脉冲动画效果 */
|
296
|
-
.progress-bar .progress.animated {
|
297
|
-
/* 动画:名称 时长 缓动函数 延迟 次数 方向 填充模式 播放状态 */
|
298
|
-
animation: pulse-lr 4s linear infinite alternate; /* 添加 alternate */
|
299
|
-
/* 注意:你也可以调整时长,例如 2s 让单程更快 */
|
300
|
-
}
|
301
|
-
/* 关键帧动画 (保持 L-to-R 定义) */
|
302
|
-
@keyframes pulse-lr {
|
303
|
-
0% {
|
304
|
-
/* 脉冲背景的右边缘对齐容器右边缘 (高亮在左边缘) */
|
305
|
-
background-position: 100% 0, 0 0;
|
306
|
-
}
|
307
|
-
100% {
|
308
|
-
/* 脉冲背景的左边缘对齐容器左边缘 (高亮在右边缘) */
|
309
|
-
background-position: 0% 0, 0 0;
|
310
|
-
}
|
311
|
-
}
|
312
|
-
.progress-bar .status-text {
|
313
|
-
position: absolute;
|
314
|
-
top: 50%;
|
315
|
-
left: 15px; /* 稍微调整左边距 */
|
316
|
-
transform: translateY(-50%);
|
317
|
-
color: var(--text-color); /* 使用文本颜色变量 */
|
318
|
-
font-weight: bold;
|
319
|
-
font-size: 14px;
|
320
|
-
z-index: 2; /* 确保状态文本覆盖在进度条上 */
|
321
|
-
}
|
322
|
-
.progress-bar .percentage-text {
|
323
|
-
position: absolute;
|
324
|
-
top: 50%;
|
325
|
-
right: 15px; /* 稍微调整右边距 */
|
326
|
-
transform: translateY(-50%);
|
327
|
-
color: var(--text-color); /* 使用文本颜色变量 */
|
328
|
-
font-weight: bold;
|
329
|
-
font-size: 14px;
|
330
|
-
z-index: 2; /* 确保百分比文本覆盖在进度条上 */
|
331
|
-
}
|
332
|
-
</style>
|
333
|
-
<script src="https://cdn.jsdelivr.net/gh/davidshimjs/qrcodejs/qrcode.min.js"></script>
|
7
|
+
<link rel="stylesheet" href="/templates/css/index.css" />
|
8
|
+
<link rel="icon" href="favicon.ico" type="image/x-icon">
|
334
9
|
</head>
|
335
10
|
<body>
|
336
11
|
<!-- 菜单栏 -->
|
@@ -363,15 +38,14 @@
|
|
363
38
|
<!-- 消息滚动显示页面 -->
|
364
39
|
<section id="pageMessage">
|
365
40
|
<h2>Podflow 运行情况</h2>
|
366
|
-
</div>
|
367
41
|
<form>
|
368
|
-
<label>主进度条:</label>
|
369
|
-
<div class="
|
370
|
-
<div class="progress animated" id="mainProgress"></div>
|
371
|
-
<div class="status-text" id="progressStatus">准备中</div>
|
372
|
-
<div class="percentage-text" id="progressPercentage">0.00%</div>
|
42
|
+
<label>主进度条:</label><br>
|
43
|
+
<div class="pb-bar" id="mainProgressBar">
|
44
|
+
<div class="pb-progress pb-animated" id="mainProgress"></div>
|
45
|
+
<div class="pb-status-text" id="progressStatus">准备中</div>
|
46
|
+
<div class="pb-percentage-text" id="progressPercentage">0.00%</div>
|
373
47
|
</div>
|
374
|
-
<label id="downloadLabel"
|
48
|
+
<label id="downloadLabel"></label><br>
|
375
49
|
<div class="common-area" id="messageDownload"></div>
|
376
50
|
<label>构建服务:</label><br>
|
377
51
|
<div class="common-area" id="messageArea"></div>
|
@@ -380,325 +54,7 @@
|
|
380
54
|
</form>
|
381
55
|
</section>
|
382
56
|
</main>
|
383
|
-
<script>
|
384
|
-
|
385
|
-
// 缓存常用节点
|
386
|
-
const menu = document.getElementById('menu');
|
387
|
-
const toggleMenuBtn = document.getElementById('toggleMenu');
|
388
|
-
const pages = {
|
389
|
-
pageChannel: document.getElementById('pageChannel'),
|
390
|
-
pageMessage: document.getElementById('pageMessage')
|
391
|
-
};
|
392
|
-
const inputForm = document.getElementById('inputForm');
|
393
|
-
const inputOutput = document.getElementById('inputOutput');
|
394
|
-
const pasteBtn = document.getElementById('pasteBtn');
|
395
|
-
const copyBtn = document.getElementById('copyBtn');
|
396
|
-
const clearBtn = document.getElementById('clearBtn');
|
397
|
-
// 缓存进度条和文本元素
|
398
|
-
const mainProgress = document.getElementById('mainProgress');
|
399
|
-
const progressStatus = document.getElementById('progressStatus');
|
400
|
-
const progressPercentage = document.getElementById('progressPercentage');
|
401
|
-
const messageArea = document.getElementById('messageArea');
|
402
|
-
const messageHttp = document.getElementById('messageHttp');
|
403
|
-
const messageDownload = document.getElementById('messageDownload');
|
404
|
-
|
405
|
-
let lastMessage = { podflow: [], http: [], schedule: [], download: [] };
|
406
|
-
let pollingTimer = null;
|
407
|
-
let userScrolled = false;
|
408
|
-
|
409
|
-
// 生成单个二维码的函数
|
410
|
-
function generateQRCodeForNode(container) {
|
411
|
-
const rootStyles = getComputedStyle(document.documentElement);
|
412
|
-
const textColor = rootStyles.getPropertyValue('--text-color').trim();
|
413
|
-
const inputBg = rootStyles.getPropertyValue('--input-bg').trim();
|
414
|
-
const url = container.dataset.url;
|
415
|
-
container.innerHTML = '';
|
416
|
-
if (url) {
|
417
|
-
new QRCode(container, {
|
418
|
-
text: url,
|
419
|
-
width: 220,
|
420
|
-
height: 220,
|
421
|
-
colorDark: textColor,
|
422
|
-
colorLight: inputBg,
|
423
|
-
correctLevel: QRCode.CorrectLevel.L
|
424
|
-
});
|
425
|
-
} else {
|
426
|
-
container.textContent = 'URL 未提供';
|
427
|
-
}
|
428
|
-
}
|
429
|
-
|
430
|
-
// 菜单切换函数
|
431
|
-
function toggleMenu() {
|
432
|
-
menu.classList.toggle('hidden');
|
433
|
-
if (menu.classList.contains('hidden')) {
|
434
|
-
toggleMenuBtn.style.left = '0px';
|
435
|
-
toggleMenuBtn.textContent = '❯';
|
436
|
-
} else {
|
437
|
-
toggleMenuBtn.style.left = 'var(--menu-width)';
|
438
|
-
toggleMenuBtn.textContent = '❮';
|
439
|
-
}
|
440
|
-
}
|
441
|
-
|
442
|
-
// 根据页面标识显示对应面板
|
443
|
-
function showPage(pageId) {
|
444
|
-
Object.values(pages).forEach(page => page.style.display = 'none');
|
445
|
-
if (pages[pageId]) {
|
446
|
-
pages[pageId].style.display = 'block';
|
447
|
-
// 手机模式下自动隐藏菜单
|
448
|
-
if (window.innerWidth <= 600 && !menu.classList.contains('hidden')) {
|
449
|
-
toggleMenu();
|
450
|
-
}
|
451
|
-
if (pageId === 'pageMessage') {
|
452
|
-
startMessage();
|
453
|
-
updateDownloadLabelVisibility();
|
454
|
-
} else {
|
455
|
-
stopMessage();
|
456
|
-
}
|
457
|
-
}
|
458
|
-
}
|
459
|
-
|
460
|
-
// 监听滚动事件,检测用户是否手动滚动
|
461
|
-
function onUserScroll(event) {
|
462
|
-
const element = event.target;
|
463
|
-
// 判断是否接近底部,增加一定的容差值
|
464
|
-
const nearBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 10;
|
465
|
-
userScrolled = !nearBottom;
|
466
|
-
}
|
467
|
-
|
468
|
-
// 轮询消息更新,更新 messageArea 与 messageHttp
|
469
|
-
function getMessages() {
|
470
|
-
fetch('message')
|
471
|
-
.then(response => response.json()) // 解析 JSON 数据
|
472
|
-
.then(data => {
|
473
|
-
if (JSON.stringify(data) !== JSON.stringify(lastMessage)) {
|
474
|
-
// 更新进度条
|
475
|
-
updateProgress(data.schedule);
|
476
|
-
// 追加新消息
|
477
|
-
appendMessages(messageArea, data.podflow, lastMessage.podflow);
|
478
|
-
appendMessages(messageHttp, data.http, lastMessage.http);
|
479
|
-
lastMessage = data;
|
480
|
-
}
|
481
|
-
})
|
482
|
-
.catch(error => console.error('获取消息失败:', error));
|
483
|
-
}
|
484
|
-
|
485
|
-
// 更新进度条并显示状态和百分比
|
486
|
-
function updateProgress(scheduleData) {
|
487
|
-
// 检查 schedule 数据是否存在且长度为 2
|
488
|
-
if (scheduleData && scheduleData.length === 2) {
|
489
|
-
const [status, progress] = scheduleData; // 直接解构数组
|
490
|
-
if (status === "准备中" || status === "构建中") {
|
491
|
-
mainProgress.style.width = `${progress * 100}%`;
|
492
|
-
progressStatus.textContent = status; // 显示状态
|
493
|
-
progressPercentage.textContent = `${(progress * 100).toFixed(2)}%`; // 显示百分比,保留两位小数
|
494
|
-
} else if (status === "已完成") {
|
495
|
-
mainProgress.style.width = '100%';
|
496
|
-
progressStatus.textContent = '已完成'; // 显示完成状态
|
497
|
-
progressPercentage.textContent = '100.0%'; // 显示百分比
|
498
|
-
}
|
499
|
-
}
|
500
|
-
}
|
501
|
-
|
502
|
-
function createMessageElement(message) {
|
503
|
-
const p = document.createElement('p');
|
504
|
-
p.innerHTML = message;
|
505
|
-
p.className = 'message';
|
506
|
-
return p;
|
507
|
-
}
|
508
|
-
|
509
|
-
function processQRCodeContainers(p) {
|
510
|
-
const qrContainers = p.querySelectorAll('.qrcode-container');
|
511
|
-
qrContainers.forEach(container => {
|
512
|
-
// 判断当前容器是否有 data-url 属性,并且值不为空
|
513
|
-
if (container.dataset.url) {
|
514
|
-
generateQRCodeForNode(container);
|
515
|
-
} else {
|
516
|
-
// 如果没有 data-url 或值为空,可以执行其他操作,例如输出提示信息
|
517
|
-
console.log('容器中未提供 URL,跳过二维码生成:', container);
|
518
|
-
container.textContent = '未提供二维码 URL'; // 可选:在容器中显示提示
|
519
|
-
}
|
520
|
-
});
|
521
|
-
}
|
522
|
-
|
523
|
-
// 修改后的 appendMessages 函数:先生成消息节点内的二维码,再将节点追加或替换到消息容器中
|
524
|
-
function appendMessages(container, newMessages, oldMessages) {
|
525
|
-
// 判断当前是否在底部
|
526
|
-
const wasAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 10;
|
527
|
-
// 较为简单的情况:两数组长度相同且有内容,只比较最后四项
|
528
|
-
if (newMessages.length === oldMessages.length && newMessages.length > 0) {
|
529
|
-
let replaceCount;
|
530
|
-
if (newMessages[newMessages.length - 1].includes("未扫描") ||
|
531
|
-
newMessages[newMessages.length - 1].includes("二维码超时, 请重试")) {
|
532
|
-
replaceCount = Math.min(4, newMessages.length);
|
533
|
-
} else {
|
534
|
-
replaceCount = 1;
|
535
|
-
}
|
536
|
-
for (let i = 0; i < replaceCount; i++) {
|
537
|
-
// 计算从后向前的索引位置
|
538
|
-
const index = newMessages.length - 1 - i;
|
539
|
-
const newMessage = newMessages[index];
|
540
|
-
const oldMessage = oldMessages[index];
|
541
|
-
if (newMessage !== oldMessage) {
|
542
|
-
// 先创建消息节点
|
543
|
-
const p = createMessageElement(newMessage);
|
544
|
-
// 在插入前先处理二维码生成
|
545
|
-
processQRCodeContainers(p);
|
546
|
-
// 替换到容器中 - 注意这里要找到对应位置的子元素
|
547
|
-
const childToReplace = container.children[index];
|
548
|
-
if (childToReplace) {
|
549
|
-
container.replaceChild(p, childToReplace);
|
550
|
-
}
|
551
|
-
}
|
552
|
-
}
|
553
|
-
} else {
|
554
|
-
// 当 newMessages 与 oldMessages 数量不一致时
|
555
|
-
// 如果 oldMessages 存在数据,先替换容器中最后一项对应的消息
|
556
|
-
if (oldMessages.length > 0) {
|
557
|
-
const replaceIndex = oldMessages.length - 1;
|
558
|
-
const p = createMessageElement(newMessages[replaceIndex]);
|
559
|
-
// 先生成二维码
|
560
|
-
processQRCodeContainers(p);
|
561
|
-
const lastChild = container.lastElementChild;
|
562
|
-
if (lastChild) {
|
563
|
-
container.replaceChild(p, lastChild);
|
564
|
-
} else {
|
565
|
-
container.appendChild(p);
|
566
|
-
}
|
567
|
-
}
|
568
|
-
// 再追加从 oldMessages.length 开始的后续消息
|
569
|
-
newMessages.slice(oldMessages.length).forEach(msg => {
|
570
|
-
const p = createMessageElement(msg);
|
571
|
-
// 先生成二维码
|
572
|
-
processQRCodeContainers(p);
|
573
|
-
// 插入容器中
|
574
|
-
container.appendChild(p);
|
575
|
-
});
|
576
|
-
}
|
577
|
-
// 如果之前在底部且用户没有主动滚动,则自动滚动到底部
|
578
|
-
if (wasAtBottom && !userScrolled) {
|
579
|
-
container.scrollTop = container.scrollHeight;
|
580
|
-
}
|
581
|
-
}
|
582
|
-
|
583
|
-
// 判断是否隐藏下载进度
|
584
|
-
function updateDownloadLabelVisibility() {
|
585
|
-
const messageDownload = document.getElementById('messageDownload');
|
586
|
-
const downloadLabel = document.getElementById('downloadLabel');
|
587
|
-
if (messageDownload.textContent.trim() === '') {
|
588
|
-
// 没有内容,隐藏label
|
589
|
-
downloadLabel.style.display = 'none';
|
590
|
-
} else {
|
591
|
-
// 有内容,显示label
|
592
|
-
downloadLabel.style.display = '';
|
593
|
-
}
|
594
|
-
}
|
595
|
-
|
596
|
-
// 启动消息轮询
|
597
|
-
function startMessage() {
|
598
|
-
getMessages();
|
599
|
-
pollingTimer = setInterval(getMessages, 250);
|
600
|
-
}
|
601
|
-
|
602
|
-
// 停止消息轮询
|
603
|
-
function stopMessage() {
|
604
|
-
if (pollingTimer !== null) {
|
605
|
-
clearInterval(pollingTimer);
|
606
|
-
pollingTimer = null;
|
607
|
-
}
|
608
|
-
}
|
609
|
-
|
610
|
-
// 滚动事件监听器
|
611
|
-
messageArea.addEventListener('scroll', onUserScroll);
|
612
|
-
messageHttp.addEventListener('scroll', onUserScroll);
|
613
|
-
messageDownload.addEventListener('scroll', onUserScroll);
|
614
|
-
|
615
|
-
// 初始化默认页面
|
616
|
-
showPage('pageMessage');
|
617
|
-
|
618
|
-
// 表单异步提交(获取 Channel-ID)
|
619
|
-
inputForm && inputForm.addEventListener('submit', function(event) {
|
620
|
-
event.preventDefault();
|
621
|
-
const content = inputOutput.value;
|
622
|
-
fetch('getid', {
|
623
|
-
method: 'POST',
|
624
|
-
headers: { 'Content-Type': 'application/json' },
|
625
|
-
body: JSON.stringify({ content })
|
626
|
-
})
|
627
|
-
.then(response => {
|
628
|
-
if (!response.ok) {
|
629
|
-
throw new Error('网络响应异常');
|
630
|
-
}
|
631
|
-
return response.json();
|
632
|
-
})
|
633
|
-
.then(data => inputOutput.value = data.response)
|
634
|
-
.catch(error => {
|
635
|
-
console.error('请求失败:', error);
|
636
|
-
alert('请求失败,请稍后重试!');
|
637
|
-
});
|
638
|
-
});
|
639
|
-
|
640
|
-
// 粘贴功能
|
641
|
-
pasteBtn.addEventListener('click', function() {
|
642
|
-
if (navigator.clipboard && navigator.clipboard.readText) {
|
643
|
-
navigator.clipboard.readText().then(text => {
|
644
|
-
inputOutput.value = text;
|
645
|
-
inputOutput.focus();
|
646
|
-
}).catch(err => {
|
647
|
-
console.warn("剪贴板读取失败:", err);
|
648
|
-
alert("无法读取剪贴板,请手动粘贴!");
|
649
|
-
});
|
650
|
-
} else {
|
651
|
-
try {
|
652
|
-
inputOutput.focus();
|
653
|
-
document.execCommand('paste');
|
654
|
-
} catch (err) {
|
655
|
-
console.warn("execCommand 粘贴失败:", err);
|
656
|
-
alert("您的浏览器不支持自动粘贴,请手动操作!");
|
657
|
-
}
|
658
|
-
}
|
659
|
-
});
|
660
|
-
|
661
|
-
// 复制功能
|
662
|
-
copyBtn.addEventListener('click', function() {
|
663
|
-
if (navigator.clipboard && navigator.clipboard.writeText) {
|
664
|
-
navigator.clipboard.writeText(inputOutput.value).catch(err => {
|
665
|
-
console.warn("复制失败:", err);
|
666
|
-
alert("无法复制,请手动选择文本后按 Ctrl+C 复制!");
|
667
|
-
});
|
668
|
-
} else {
|
669
|
-
try {
|
670
|
-
inputOutput.select();
|
671
|
-
document.execCommand('copy');
|
672
|
-
} catch (err) {
|
673
|
-
console.warn("execCommand 复制失败:", err);
|
674
|
-
alert("您的浏览器不支持复制,请手动操作!");
|
675
|
-
}
|
676
|
-
}
|
677
|
-
});
|
678
|
-
|
679
|
-
// 清空输入框
|
680
|
-
clearBtn.addEventListener('click', function() {
|
681
|
-
inputOutput.value = '';
|
682
|
-
});
|
683
|
-
|
684
|
-
// 菜单项点击事件委托
|
685
|
-
menu.addEventListener('click', function(event) {
|
686
|
-
const target = event.target;
|
687
|
-
if (target.tagName.toLowerCase() === 'li' && target.dataset.page) {
|
688
|
-
showPage(target.dataset.page);
|
689
|
-
}
|
690
|
-
});
|
691
|
-
|
692
|
-
// 菜单切换按钮事件绑定
|
693
|
-
toggleMenuBtn.addEventListener('click', toggleMenu);
|
694
|
-
|
695
|
-
// 针对手机端,初始化时隐藏菜单
|
696
|
-
if (window.innerWidth <= 600) {
|
697
|
-
menu.classList.add('hidden');
|
698
|
-
toggleMenuBtn.style.left = '0px';
|
699
|
-
toggleMenuBtn.textContent = '❯';
|
700
|
-
}
|
701
|
-
})();
|
702
|
-
</script>
|
57
|
+
<script src="/templates/js/index.js"></script>
|
58
|
+
<script src="/templates/js/qrcode.min.js"></script>
|
703
59
|
</body>
|
704
60
|
</html>
|