tt-help-cli-ycl 1.3.8 → 1.3.9
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.
- package/package.json +1 -1
- package/src/watch/public/index.html +36 -2
- package/src/watch/server.mjs +16 -0
package/package.json
CHANGED
|
@@ -108,6 +108,7 @@
|
|
|
108
108
|
.controls { flex-wrap: wrap; gap: 6px; }
|
|
109
109
|
.controls input { flex: 0 0 100%; width: 100%; }
|
|
110
110
|
.controls button { flex: 0 0 calc(33.33% - 4px); min-width: 0; text-align: center; white-space: nowrap; font-size: 11px; padding: 8px 4px; }
|
|
111
|
+
#batchResetBtn { flex: 0 0 100% !important; font-size: 12px !important; padding: 8px 12px !important; }
|
|
111
112
|
.controls select { flex: 0 0 100%; width: 100%; }
|
|
112
113
|
.table-scroll { max-height: none; overflow: visible; }
|
|
113
114
|
table, thead, tbody, th, td, tr { display: block; }
|
|
@@ -169,6 +170,7 @@
|
|
|
169
170
|
<button data-filter="error" onclick="setFilter('error')">错误</button>
|
|
170
171
|
<button data-filter="restricted" onclick="setFilter('restricted')">受限</button>
|
|
171
172
|
<button data-filter="target" onclick="setFilter('target')" style="background:#7c3aed;color:#fff">目标用户</button>
|
|
173
|
+
<button id="batchResetBtn" onclick="batchResetErrors()" style="display:none;padding:6px 10px;border:1px solid #f87171;border-radius:6px;background:transparent;color:#f87171;font-size:12px;cursor:pointer;font-weight:600;transition:all 0.2s;white-space:nowrap;">↻ 批量重新处理 (<span id="batchResetCount">0</span>)</button>
|
|
172
174
|
<select id="locationFilter" onchange="onLocationChange()" style="padding:6px 10px;border:1px solid #333;border-radius:6px;background:#2a2a3a;color:#ccc;font-size:12px;cursor:pointer;outline:none;">
|
|
173
175
|
<option value="">全部国家</option>
|
|
174
176
|
</select>
|
|
@@ -324,6 +326,10 @@ function renderTable(users) {
|
|
|
324
326
|
</tr>`;
|
|
325
327
|
}).join('');
|
|
326
328
|
|
|
329
|
+
const errorCount = users.filter(u => u.status === 'error').length;
|
|
330
|
+
const countEl = document.getElementById('batchResetCount');
|
|
331
|
+
if (countEl) countEl.textContent = errorCount;
|
|
332
|
+
|
|
327
333
|
prevUserMap = newUserMap;
|
|
328
334
|
}
|
|
329
335
|
|
|
@@ -344,6 +350,8 @@ function setFilter(f) {
|
|
|
344
350
|
document.querySelectorAll('.controls button').forEach(b => {
|
|
345
351
|
b.classList.toggle('active', b.dataset.filter === f);
|
|
346
352
|
});
|
|
353
|
+
const btn = document.getElementById('batchResetBtn');
|
|
354
|
+
btn.style.display = f === 'error' ? '' : 'none';
|
|
347
355
|
fetchUsers();
|
|
348
356
|
}
|
|
349
357
|
|
|
@@ -548,6 +556,32 @@ async function resetJob(uniqueId) {
|
|
|
548
556
|
}
|
|
549
557
|
}
|
|
550
558
|
|
|
559
|
+
async function batchResetErrors() {
|
|
560
|
+
const errorUsers = currentUsers.filter(u => u.status === 'error');
|
|
561
|
+
if (errorUsers.length === 0) {
|
|
562
|
+
showToast('\u6ca1\u6709\u9700\u8981\u91cd\u7f6e\u7684\u9519\u8bef\u7528\u6237', true);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
const userIds = errorUsers.map(u => u.uniqueId);
|
|
566
|
+
try {
|
|
567
|
+
const res = await fetch('/api/jobs/batch-reset', {
|
|
568
|
+
method: 'POST',
|
|
569
|
+
headers: { 'Content-Type': 'application/json' },
|
|
570
|
+
body: JSON.stringify({ userIds })
|
|
571
|
+
});
|
|
572
|
+
const data = await res.json();
|
|
573
|
+
if (data.error) {
|
|
574
|
+
showToast(data.error, true);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
showToast(`\u5df2\u91cd\u7f6e ${data.reset} / ${data.total} \u4e2a\u7528\u6237`);
|
|
578
|
+
fetchUsers();
|
|
579
|
+
fetchStats();
|
|
580
|
+
} catch (e) {
|
|
581
|
+
showToast('\u6279\u91cf\u91cd\u7f6e\u5931\u8d25: ' + e.message, true);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
551
585
|
document.getElementById('statTargetCard').addEventListener('click', async () => {
|
|
552
586
|
try {
|
|
553
587
|
const res = await fetch('/api/target-users');
|
|
@@ -575,8 +609,8 @@ document.getElementById('statTargetCard').addEventListener('click', async () =>
|
|
|
575
609
|
|
|
576
610
|
fetchStats();
|
|
577
611
|
fetchUsers();
|
|
578
|
-
setInterval(fetchStats,
|
|
579
|
-
setInterval(fetchUsers,
|
|
612
|
+
setInterval(fetchStats, 10000);
|
|
613
|
+
setInterval(fetchUsers, 10000);
|
|
580
614
|
</script>
|
|
581
615
|
</body>
|
|
582
616
|
</html>
|
package/src/watch/server.mjs
CHANGED
|
@@ -187,6 +187,22 @@ export function startWatchServer(outputFile, port = 3000, existingStore) {
|
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
+
if (req.method === 'POST' && routePath === '/api/jobs/batch-reset') {
|
|
191
|
+
const body = await readBody(req);
|
|
192
|
+
const ids = Array.isArray(body.userIds) ? body.userIds : [];
|
|
193
|
+
if (ids.length === 0) {
|
|
194
|
+
sendJSON(res, 400, { error: 'userIds 不能为空' });
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
let count = 0;
|
|
198
|
+
for (const uid of ids) {
|
|
199
|
+
const ret = store.resetJob(uid);
|
|
200
|
+
if (ret.saved) count++;
|
|
201
|
+
}
|
|
202
|
+
sendJSON(res, 200, { reset: count, total: ids.length });
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
190
206
|
const jobPinMatch = routePath.match(/^\/api\/job\/([^/]+)\/pin$/);
|
|
191
207
|
if (req.method === 'POST' && jobPinMatch) {
|
|
192
208
|
const uniqueId = jobPinMatch[1];
|