@wmj-code/clone_npm 1.0.0

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.
@@ -0,0 +1,741 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <title>Clone-NPM 配置编辑器</title>
7
+ <link rel="stylesheet" href="main.css">
8
+ </head>
9
+
10
+ <body>
11
+ <!-- 右上角已勾选执行项的悬浮面板 -->
12
+ <div class="enabled-items-panel">
13
+ <div class="title">✅ 已勾选执行的项目</div>
14
+ <div id="enabledItemsList" class="empty-tip">暂无勾选的执行项</div>
15
+ </div>
16
+ <div class="terminal-container">
17
+ <!-- 新增:终端输出面板 -->
18
+ <div class="terminal-panel" id="terminalPanel" style="width: 530px;position: fixed;top:0;left:0;">
19
+ <div class="terminal-header">
20
+ <span>命令执行日志</span>
21
+ <span>
22
+ <button class="clear-btn" id="clearTerminal1">关闭</button>
23
+ <button class="clear-btn" id="clearTerminal">清空日志</button>
24
+ </span>
25
+ </div>
26
+ <div class="log" id="terminalLog"></div>
27
+ </div>
28
+ <div class="container" id="mainContainer" style="height: calc(100vh - 0px);overflow: auto;">
29
+ <div class="header">
30
+ <h2>Clone-NPM 配置管理</h2> <a href="/ai-models.html"
31
+ style="color: #9C27B0; text-decoration: none; font-size: 14px; cursor: pointer;">🤖 AIModelIDs</a>
32
+ </div>
33
+ <!-- 编辑器路径配置 -->
34
+ <div class="editor-path">
35
+ <label>项目运行IDE:</label>
36
+ <select id="codingEditPath" style="padding: 5px; margin-right: 10px;">
37
+ <option value="">请选择编辑器路径</option>
38
+ </select>
39
+ <!-- 可以在电脑本地选择 -->
40
+ <!-- <button class="btn-select" id="selectCodingEditPath">选择</button> -->
41
+ </div>
42
+
43
+ <!-- 编辑器路径列表 -->
44
+ <div class="editor-path-list" style="margin-top: 20px; padding: 15px; border: 1px solid #ddd; border-radius: 5px;">
45
+ <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
46
+ <label style="font-weight: bold;">编辑器路径选项:</label>
47
+ <button class="btn-primary" id="addEditPath">新增</button>
48
+ </div>
49
+ <div id="editPathList" style="margin-top: 10px;"></div>
50
+ </div>
51
+
52
+ <div style="display: flex;">
53
+ <!-- 项目名称标题tab -->
54
+ <div class="pName-tabs" style="width: 200px;margin-right: 10px; border: 1px solid #ccc;padding: 5px;">
55
+ <div id="pName-tabs-list"></div>
56
+ </div>
57
+ <!-- 配置项列表 -->
58
+ <div id="configList" style="flex: 1;"></div>
59
+ </div>
60
+
61
+
62
+ <!-- 提示信息 -->
63
+ <div class="msg" id="msg"></div>
64
+ </div>
65
+ </div>
66
+
67
+
68
+ <!-- 右侧悬浮按钮 -->
69
+ <button class="btn-primary floating-btn" id="addItem" style="top: 55%;">新增配置</button>
70
+ <button class="btn-primary floating-btn" id="saveAll" style="top: 65%;">保存配置</button>
71
+ <button class="btn-primary floating-btn" id="refresh" style="top: 75%;">刷新配置</button>
72
+
73
+ <!-- 回到顶部按钮 -->
74
+ <button class="back-to-top" id="backToTop">↑</button>
75
+
76
+ <!-- 新增:功能按钮悬浮栏 -->
77
+ <div class="function-buttons">
78
+ <button class="function-btn" id="btn-1">1. 下载代码+切换分支+打开文件夹</button>
79
+ <button class="function-btn" id="btn-2">2. 清除依赖+下载依赖</button>
80
+ <button class="function-btn" id="btn-3">3. 批量打开项目文件夹</button>
81
+ <button class="function-btn" id="btn-4">4. 拉取最新代码+npm run dev</button>
82
+ <button class="function-btn" id="btn-5">5. 全部流程(1+2)</button>
83
+ </div>
84
+
85
+
86
+
87
+ <script>
88
+ // 基础接口地址
89
+ const API_BASE = 'http://localhost:3000/api';
90
+ let itemIdCounter = 0;
91
+ // 新增:终端相关DOM
92
+ const terminalPanel = document.getElementById('terminalPanel');
93
+ const terminalLog = document.getElementById('terminalLog');
94
+ const clearTerminalBtn = document.getElementById('clearTerminal');
95
+ const closeTerminalBtn = document.getElementById('clearTerminal1');
96
+ const mainContainer = document.getElementById('mainContainer');
97
+ closeTerminalBtn.addEventListener('click', () => {
98
+ terminalPanel.style.display = 'none';
99
+ mainContainer.style.marginLeft = '0';
100
+ });
101
+ // 新增:是否正在执行命令(防止重复点击)
102
+ let isExecuting = false;
103
+
104
+ // 显示提示信息
105
+ function showMsg(text, isSuccess = true) {
106
+ const msgEl = document.getElementById('msg');
107
+ msgEl.textContent = text;
108
+ msgEl.className = isSuccess ? 'msg msg-success' : 'msg msg-error';
109
+ msgEl.style.display = 'block';
110
+ setTimeout(() => msgEl.style.display = 'none', 3000);
111
+ }
112
+
113
+ // 滚动到指定ID的配置项
114
+ function scrollToItem(itemId) {
115
+ document.querySelectorAll('.config-item').forEach(item => {
116
+ item.classList.remove('highlight');
117
+ });
118
+ const targetItem = document.getElementById(`config-item-${itemId}`);
119
+ if (targetItem) {
120
+ targetItem.scrollIntoView({
121
+ behavior: 'smooth',
122
+ block: 'center'
123
+ });
124
+ targetItem.classList.add('highlight');
125
+ setTimeout(() => {
126
+ targetItem.classList.remove('highlight');
127
+ }, 1500);
128
+ }
129
+ }
130
+
131
+ // 更新右上角已勾选执行项的面板
132
+ function updateEnabledItemsPanel() {
133
+ const enabledItemsListEl = document.getElementById('enabledItemsList');
134
+ const enabledItems = [];
135
+ document.querySelectorAll('.config-item').forEach(item => {
136
+ const enable = item.querySelector('.enable-checkbox').checked;
137
+ if (enable) {
138
+ const itemId = item.id.split('-')[2];
139
+ const pName = item.querySelector('.pName').value.trim() || '未命名项目';
140
+ const projectName = item.querySelector('.projectName').value.trim() || '未命名系统';
141
+ const gitUrl = item.querySelector('.gitUrl').value.trim() || '无Git地址';
142
+ enabledItems.push({
143
+ itemId,
144
+ pName,
145
+ projectName,
146
+ gitUrl
147
+ });
148
+ }
149
+ });
150
+ if (enabledItems.length === 0) {
151
+ enabledItemsListEl.className = 'empty-tip';
152
+ enabledItemsListEl.innerHTML = '暂无勾选的执行项';
153
+ } else {
154
+ enabledItemsListEl.className = '';
155
+ enabledItemsListEl.innerHTML = enabledItems.map(item =>
156
+ `<div class="item" onclick="scrollToItem(${item.itemId})">
157
+ <span class="name">${item.projectName}</span>
158
+ </div>`
159
+ ).join('');
160
+ }
161
+ }
162
+
163
+ // 更新项目名称标签列表
164
+ function updatePNameTabs() {
165
+ const pNameTabsListEl = document.getElementById('pName-tabs-list');
166
+ const pNameMap = new Map();
167
+
168
+ // 收集所有项目名称并去重
169
+ document.querySelectorAll('.config-item').forEach(item => {
170
+ const pName = item.querySelector('.pName').value.trim() || '未命名项目';
171
+ const itemId = item.id.split('-')[2];
172
+
173
+ if (!pNameMap.has(pName)) {
174
+ pNameMap.set(pName, []);
175
+ }
176
+ pNameMap.get(pName).push(itemId);
177
+ });
178
+
179
+ if (pNameMap.size === 0) {
180
+ pNameTabsListEl.innerHTML = '<div class="empty-tip">暂无项目</div>';
181
+ } else {
182
+ pNameTabsListEl.innerHTML = Array.from(pNameMap.entries()).map(([pName, itemIds]) => {
183
+ return `<div class="pName-tab" data-pName="${pName}" onclick="filterByPName('${pName}')">
184
+ ${pName}
185
+ <span class="count">(${itemIds.length})</span>
186
+ </div>`;
187
+ }).join('');
188
+ }
189
+ }
190
+
191
+ // 根据项目名称筛选配置项
192
+ function filterByPName(targetPName) {
193
+ // 将项目名称是targetPName的放在列表前面,并保存配置项
194
+ const configListEl = document.getElementById('configList');
195
+ const allItems = Array.from(document.querySelectorAll('.config-item'));
196
+
197
+ // 将配置项分为两组:匹配的和不匹配的
198
+ const matchedItems = [];
199
+ const unmatchedItems = [];
200
+
201
+ allItems.forEach(item => {
202
+ const pName = item.querySelector('.pName').value.trim() || '未命名项目';
203
+ if (pName === targetPName) {
204
+ matchedItems.push(item);
205
+ // 高亮显示匹配的配置项
206
+ item.classList.add('highlight');
207
+ setTimeout(() => {
208
+ item.classList.remove('highlight');
209
+ }, 1500);
210
+ } else {
211
+ unmatchedItems.push(item);
212
+ }
213
+ });
214
+
215
+ // 清空列表并重新添加配置项(先添加匹配的,再添加不匹配的)
216
+ configListEl.innerHTML = '';
217
+ matchedItems.forEach(item => configListEl.appendChild(item));
218
+ unmatchedItems.forEach(item => configListEl.appendChild(item));
219
+
220
+ // 滚动到第一个匹配的配置项
221
+ if (matchedItems.length > 0) {
222
+ matchedItems[0].scrollIntoView({
223
+ behavior: 'smooth',
224
+ block: 'center'
225
+ });
226
+ }
227
+
228
+ // 保存排序后的配置项
229
+ saveConfig();
230
+ // loadConfig();
231
+ }
232
+
233
+ // 创建单个配置项DOM
234
+ function createConfigItem(data = {}) {
235
+ const itemId = itemIdCounter++;
236
+ const item = document.createElement('div');
237
+ item.id = `config-item-${itemId}`;
238
+ item.className = 'config-item';
239
+ item.innerHTML = `
240
+ <div class="row">
241
+ <label>项目名称:</label>
242
+ <input type="text" class="pName" value="${data.pName || ''}" placeholder="必填:如 安徽省数字地矿项目">
243
+ </div>
244
+ <div class="row">
245
+ <label>系统名称:</label>
246
+ <input type="text" class="projectName" value="${data.projectName || ''}" placeholder="必填:如 组件库-core-v3">
247
+ </div>
248
+ <div class="row">
249
+ <label>是否执行:</label>
250
+ <input type="checkbox" class="enable-checkbox" ${data.enable ? 'checked' : ''}>
251
+ <span style="color: #666; font-size: 12px;">勾选后命令会执行该配置</span>
252
+ </div>
253
+ <div class="row">
254
+ <label>Gitlab地址:</label>
255
+ <input type="text" class="gitUrl" value="${data.gitUrl || ''}" placeholder="必填:http://xxx.git">
256
+ <button class="btn-select gitUrl-open" style="margin-left: 5px;">打开</button>
257
+ </div>
258
+ <div class="row">
259
+ <label>分支:</label>
260
+ <input type="text" class="branch" value="${data.branch || 'master'}" placeholder="默认:master">
261
+ </div>
262
+ <div class="row">
263
+ <label>保存路径:</label>
264
+ <input type="text" class="savePath" value="${data.savePath || ''}" placeholder="必填:D:\桌面\项目">
265
+ </div>
266
+ <div class="row">
267
+ <label>代码子路径:</label>
268
+ <input type="text" class="codePath" value="${data.codePath || ''}" placeholder="可选:\项目名\webapp">
269
+ </div>
270
+ <div class="row">
271
+ <label>测试环境地址:</label>
272
+ <input type="text" class="testEnvUrl" value="${data.testEnvUrl || ''}" placeholder="可选:http://测试环境地址">
273
+ <button class="btn-select testEnvUrl-open" style="margin-left: 5px;">打开</button>
274
+ </div>
275
+ <div class="row">
276
+ <label>正式环境地址:</label>
277
+ <input type="text" class="prodEnvUrl" value="${data.prodEnvUrl || ''}" placeholder="可选:http://正式环境地址">
278
+ <button class="btn-select prodEnvUrl-open" style="margin-left: 5px;">打开</button>
279
+ </div>
280
+ <div class="row">
281
+ <button class="btn-danger delete-btn">删除</button>
282
+ </div>
283
+ `;
284
+ // 删除按钮事件
285
+ item.querySelector('.delete-btn').addEventListener('click', () => {
286
+ if (confirm('确定删除该配置项吗?')) {
287
+ item.remove();
288
+ updateEnabledItemsPanel();
289
+ updatePNameTabs();
290
+ }
291
+ });
292
+ // 勾选框/输入框变化事件
293
+ const enableCheckbox = item.querySelector('.enable-checkbox');
294
+ const pNameInput = item.querySelector('.pName');
295
+ const projectNameInput = item.querySelector('.projectName');
296
+ const gitUrlInput = item.querySelector('.gitUrl');
297
+ enableCheckbox.addEventListener('change', () => {
298
+ updateEnabledItemsPanel();
299
+ updatePNameTabs();
300
+ });
301
+ projectNameInput.addEventListener('input', updateEnabledItemsPanel);
302
+ gitUrlInput.addEventListener('input', updateEnabledItemsPanel);
303
+ pNameInput.addEventListener('input', () => {
304
+ updateEnabledItemsPanel();
305
+ updatePNameTabs();
306
+ });
307
+
308
+ // Gitlab地址打开按钮事件
309
+ const gitUrlOpenBtn = item.querySelector('.gitUrl-open');
310
+ gitUrlOpenBtn.addEventListener('click', () => {
311
+ const gitUrl = gitUrlInput.value.trim();
312
+ if (gitUrl) {
313
+ window.open(gitUrl, '_blank');
314
+ } else {
315
+ showMsg('请输入 Gitlab 地址', false);
316
+ }
317
+ });
318
+
319
+ // 系统测试环境地址打开按钮事件
320
+ const testEnvUrlInput = item.querySelector('.testEnvUrl');
321
+ const testEnvUrlOpenBtn = item.querySelector('.testEnvUrl-open');
322
+ testEnvUrlOpenBtn.addEventListener('click', () => {
323
+ const testEnvUrl = testEnvUrlInput.value.trim();
324
+ if (testEnvUrl) {
325
+ window.open(testEnvUrl, '_blank');
326
+ } else {
327
+ showMsg('请输入系统测试环境地址', false);
328
+ }
329
+ });
330
+
331
+ // 正式环境地址打开按钮事件
332
+ const prodEnvUrlInput = item.querySelector('.prodEnvUrl');
333
+ const prodEnvUrlOpenBtn = item.querySelector('.prodEnvUrl-open');
334
+ prodEnvUrlOpenBtn.addEventListener('click', () => {
335
+ const prodEnvUrl = prodEnvUrlInput.value.trim();
336
+ if (prodEnvUrl) {
337
+ window.open(prodEnvUrl, '_blank');
338
+ } else {
339
+ showMsg('请输入正式环境地址', false);
340
+ }
341
+ });
342
+ return item;
343
+ }
344
+
345
+ // 创建编辑器路径项
346
+ function createEditPathItem(path = '') {
347
+ const item = document.createElement('div');
348
+ item.className = 'edit-path-item';
349
+ item.style.display = 'flex';
350
+ item.style.alignItems = 'center';
351
+ item.style.marginBottom = '10px';
352
+ item.style.padding = '8px';
353
+ item.style.border = '1px solid #eee';
354
+ item.style.borderRadius = '4px';
355
+ item.innerHTML = `
356
+ <input type="text" class="edit-path-input" value="${path}" placeholder="编辑器路径" style="flex: 1; margin-right: 10px; padding: 5px;">
357
+ <button class="btn-select edit-path-select" style="margin-right: 5px;">选择</button>
358
+ <button class="btn-danger edit-path-delete" style="padding: 5px 10px;">删除</button>
359
+ `;
360
+
361
+ // 输入框变化事件
362
+ const inputEl = item.querySelector('.edit-path-input');
363
+ inputEl.addEventListener('input', () => {
364
+ updateEditPathSelect();
365
+ });
366
+
367
+ // 选择按钮事件
368
+ item.querySelector('.edit-path-select').addEventListener('click', async () => {
369
+ try {
370
+ const res = await fetch(`${API_BASE}/select-path`, {
371
+ method: 'POST',
372
+ headers: { 'Content-Type': 'application/json' },
373
+ body: JSON.stringify({ type: 'file' })
374
+ });
375
+ const result = await res.json();
376
+ if (result.success && result.path) {
377
+ inputEl.value = result.path;
378
+ updateEditPathSelect();
379
+ showMsg('已选择编辑器路径');
380
+ } else if (!result.path) {
381
+ showMsg('未选择任何文件', false);
382
+ }
383
+ } catch (err) {
384
+ showMsg('选择文件失败:' + err.message, false);
385
+ }
386
+ });
387
+
388
+ // 删除按钮事件
389
+ item.querySelector('.edit-path-delete').addEventListener('click', () => {
390
+ if (confirm('确定删除该编辑器路径吗?')) {
391
+ item.remove();
392
+ updateEditPathSelect();
393
+ }
394
+ });
395
+
396
+ return item;
397
+ }
398
+
399
+ // 更新编辑器路径下拉选择框
400
+ function updateEditPathSelect() {
401
+ const selectEl = document.getElementById('codingEditPath');
402
+ const currentValue = selectEl.value;
403
+
404
+ // 清空现有选项
405
+ selectEl.innerHTML = '<option value="">请选择编辑器路径</option>';
406
+
407
+ // 添加编辑器路径列表中的选项
408
+ document.querySelectorAll('.edit-path-input').forEach(input => {
409
+ const path = input.value.trim();
410
+ if (path) {
411
+ const option = document.createElement('option');
412
+ option.value = path;
413
+ option.textContent = path;
414
+ selectEl.appendChild(option);
415
+ }
416
+ });
417
+
418
+ // 保持当前选择的值
419
+ if (currentValue) {
420
+ selectEl.value = currentValue;
421
+ }
422
+ }
423
+
424
+ // 加载配置
425
+ async function loadConfig() {
426
+ try {
427
+ const res = await fetch(`${API_BASE}/config`);
428
+ if (!res.ok) throw new Error('配置加载失败');
429
+ const config = await res.json();
430
+
431
+ // 加载编辑器路径列表
432
+ const editPathListEl = document.getElementById('editPathList');
433
+ editPathListEl.innerHTML = '';
434
+ (config.CodingEditPathList || []).forEach(path => {
435
+ editPathListEl.appendChild(createEditPathItem(path));
436
+ });
437
+
438
+ // 更新下拉选择框
439
+ updateEditPathSelect();
440
+ document.getElementById('codingEditPath').value = config.CodingEditPath || '';
441
+
442
+ const configListEl = document.getElementById('configList');
443
+ configListEl.innerHTML = '';
444
+ itemIdCounter = 0;
445
+ (config.configList || []).forEach(item => {
446
+ configListEl.appendChild(createConfigItem(item));
447
+ });
448
+ updateEnabledItemsPanel();
449
+ updatePNameTabs();
450
+ showMsg('配置加载成功!');
451
+ } catch (err) {
452
+ showMsg('加载失败:' + err.message, false);
453
+ }
454
+ }
455
+
456
+ // 保存配置
457
+ async function saveConfig() {
458
+ try {
459
+ const configList = [];
460
+ document.querySelectorAll('.config-item').forEach(item => {
461
+ const enable = item.querySelector('.enable-checkbox').checked;
462
+ const pName = item.querySelector('.pName').value.trim();
463
+ const projectName = item.querySelector('.projectName').value.trim();
464
+ const gitUrl = item.querySelector('.gitUrl').value.trim();
465
+ const branch = item.querySelector('.branch').value.trim();
466
+ const savePath = item.querySelector('.savePath').value.trim();
467
+ const codePath = item.querySelector('.codePath').value.trim();
468
+ const testEnvUrl = item.querySelector('.testEnvUrl').value.trim();
469
+ const prodEnvUrl = item.querySelector('.prodEnvUrl').value.trim();
470
+ // 校验必填项
471
+ if (enable) {
472
+ if (!pName) {
473
+ throw new Error('存在勾选「执行」但未填写项目名称的配置项,请补充!');
474
+ }
475
+ if (!gitUrl) {
476
+ throw new Error('存在勾选「执行」但未填写Git地址的配置项,请补充!');
477
+ }
478
+ if (!projectName) {
479
+ throw new Error(`Git地址【${gitUrl}】勾选了执行,但未填写项目名称,请补充!`);
480
+ }
481
+ if (!savePath) {
482
+ throw new Error(`Git地址【${gitUrl}】勾选了执行,但未填写保存路径,请补充!`);
483
+ }
484
+ if (!codePath) {
485
+ throw new Error(`Git地址【${gitUrl}】勾选了执行,但未填写代码子路径,请补充!`);
486
+ }
487
+ }
488
+ configList.push({
489
+ enable,
490
+ pName,
491
+ projectName,
492
+ gitUrl,
493
+ branch,
494
+ savePath,
495
+ codePath,
496
+ testEnvUrl,
497
+ prodEnvUrl
498
+ });
499
+ });
500
+ const CodingEditPath = document.getElementById('codingEditPath').value.trim();
501
+ // 收集编辑器路径列表
502
+ const CodingEditPathList = [];
503
+ document.querySelectorAll('.edit-path-input').forEach(input => {
504
+ const path = input.value.trim();
505
+ if (path) {
506
+ CodingEditPathList.push(path);
507
+ }
508
+ });
509
+ const res = await fetch(`${API_BASE}/save`, {
510
+ method: 'POST',
511
+ headers: {
512
+ 'Content-Type': 'application/json'
513
+ },
514
+ body: JSON.stringify({
515
+ configList,
516
+ CodingEditPath,
517
+ CodingEditPathList
518
+ })
519
+ });
520
+ const result = await res.json();
521
+ if (result.success) {
522
+ showMsg('配置保存成功(已覆盖config.json)!');
523
+ } else {
524
+ throw new Error(result.error);
525
+ }
526
+ } catch (err) {
527
+ showMsg('保存失败:' + err.message, false);
528
+ }
529
+ }
530
+
531
+ // 新增:终端日志输出函数
532
+ function logToTerminal(text, type = 'info') {
533
+ // 显示终端面板
534
+ terminalPanel.style.display = 'block';
535
+ // 创建日志元素
536
+ const logItem = document.createElement('div');
537
+ logItem.className = `log log-${type}`;
538
+ // 添加时间戳
539
+ const time = new Date().toLocaleTimeString();
540
+ logItem.textContent = `[${time}] ${text}`;
541
+ // 追加到日志容器
542
+ terminalLog.appendChild(logItem);
543
+ // 自动滚动到底部
544
+ terminalLog.scrollTop = terminalLog.scrollHeight;
545
+ }
546
+
547
+ function showTerminal() {
548
+ mainContainer.style.marginLeft = '530px';
549
+ }
550
+ // 新增:执行命令的核心函数
551
+ async function executeCommand(flag) {
552
+ // 先保存配置(确保最新配置生效)
553
+ await saveConfig();
554
+ // 防止重复执行
555
+ if (isExecuting) {
556
+ showMsg('已有命令正在执行,请等待完成!', false);
557
+ return;
558
+ }
559
+ // 检查是否有勾选的配置项
560
+ const enabledItems = document.querySelectorAll('.enable-checkbox:checked');
561
+ if (enabledItems.length === 0) {
562
+ showMsg('请先勾选需要执行的配置项!', false);
563
+ return;
564
+ }
565
+
566
+ isExecuting = true;
567
+ // 禁用所有功能按钮
568
+ document.querySelectorAll('.function-btn').forEach(btn => btn.disabled = true);
569
+ // 新增:将主容器向右移动,露出终端面板
570
+ showTerminal();
571
+ // 清空并显示终端
572
+ terminalLog.innerHTML = '';
573
+ logToTerminal(`开始执行命令 [${flag}]...`, 'info');
574
+
575
+ try {
576
+ // 调用后端execute接口(需在server.js中实现)
577
+ const res = await fetch(`${API_BASE}/execute`, {
578
+ method: 'POST',
579
+ headers: {
580
+ 'Content-Type': 'application/json'
581
+ },
582
+ body: JSON.stringify({
583
+ flag: flag
584
+ })
585
+ });
586
+
587
+ // 流式读取响应(模拟终端实时输出)
588
+ const reader = res.body.getReader();
589
+ const decoder = new TextDecoder();
590
+ while (true) {
591
+ const {
592
+ done,
593
+ value
594
+ } = await reader.read();
595
+ if (done) break;
596
+ const chunk = decoder.decode(value);
597
+ if (chunk.includes('ERROR')) {
598
+ logToTerminal(chunk, 'error');
599
+ } else if (chunk.includes('SUCCESS')) {
600
+ logToTerminal(chunk, 'success');
601
+ } else {
602
+ logToTerminal(chunk, 'info');
603
+ }
604
+ }
605
+
606
+ if (!res.ok) {
607
+ throw new Error(`请求失败:${res.statusText}`);
608
+ }
609
+ logToTerminal(`命令 [${flag}] 执行完成!`, 'success');
610
+ showMsg(`命令 ${flag} 执行成功!`);
611
+ } catch (err) {
612
+ logToTerminal(`执行失败:${err.message}`, 'error');
613
+ showMsg(`命令 ${flag} 执行失败:${err.message}`, false);
614
+ } finally {
615
+ isExecuting = false;
616
+ // 启用所有功能按钮
617
+ document.querySelectorAll('.function-btn').forEach(btn => btn.disabled = false);
618
+ }
619
+ }
620
+
621
+ // 绑定回到顶部事件
622
+ const backToTopBtn = document.getElementById('backToTop');
623
+
624
+ // 项目名称标签悬浮功能
625
+ const pNameTabs = document.querySelector('.pName-tabs');
626
+ let pNameTabsOffsetTop = 0;
627
+ let mainContainerOffsetTop = 0;
628
+ let mainContainerOffsetLeft = 0;
629
+
630
+ // 初始化项目名称标签的初始位置
631
+ function initPNameTabsPosition() {
632
+ if (pNameTabs && mainContainer) {
633
+ const pNameRect = pNameTabs.getBoundingClientRect();
634
+ const mainRect = mainContainer.getBoundingClientRect();
635
+ pNameTabsOffsetTop = pNameRect.top;
636
+ mainContainerOffsetTop = mainRect.top;
637
+ mainContainerOffsetLeft = mainRect.left;
638
+ }
639
+ }
640
+ // 监听mainContainer滚动事件 backToTopBtn回到顶部按钮显示/隐藏
641
+ mainContainer.addEventListener('scroll', () => {
642
+ if (mainContainer.scrollTop > 200) {
643
+ backToTopBtn.style.display = 'block';
644
+ } else {
645
+ backToTopBtn.style.display = 'none';
646
+ }
647
+ });
648
+
649
+ // 监听窗口滚动事件
650
+ window.addEventListener('scroll', () => {
651
+ // 项目名称标签悬浮
652
+ if (pNameTabs && mainContainer) {
653
+ const mainContainerTop = mainContainer.getBoundingClientRect().top;
654
+ if (window.scrollY > (pNameTabsOffsetTop - mainContainerOffsetTop) - 20) {
655
+ pNameTabs.classList.add('fixed');
656
+ pNameTabs.style.left = mainContainerOffsetLeft + 20 + 'px';
657
+ } else {
658
+ pNameTabs.classList.remove('fixed');
659
+ pNameTabs.style.left = '';
660
+ }
661
+ }
662
+
663
+ });
664
+
665
+ // 页面加载时自动加载配置和初始化位置
666
+ window.onload = function () {
667
+ loadConfig();
668
+ initPNameTabsPosition();
669
+ };
670
+
671
+ // 窗口大小改变时重新初始化位置
672
+ window.addEventListener('resize', initPNameTabsPosition);
673
+ backToTopBtn.addEventListener('click', () => {
674
+ mainContainer.scrollTo({
675
+ top: 0,
676
+ behavior: 'smooth'
677
+ });
678
+ });
679
+
680
+ // 绑定基础按钮事件
681
+ document.getElementById('addItem').addEventListener('click', () => {
682
+ const newItem = createConfigItem({
683
+ enable: false
684
+ });
685
+ document.getElementById('configList').appendChild(newItem);
686
+ updateEnabledItemsPanel();
687
+ updatePNameTabs();
688
+ // 滚动到新添加的配置项
689
+ newItem.scrollIntoView({
690
+ behavior: 'smooth'
691
+ });
692
+ });
693
+ document.getElementById('saveAll').addEventListener('click', saveConfig);
694
+ document.getElementById('refresh').addEventListener('click', loadConfig);
695
+
696
+ // 绑定新增编辑器路径按钮事件
697
+ document.getElementById('addEditPath').addEventListener('click', () => {
698
+ const newEditPathItem = createEditPathItem('');
699
+ document.getElementById('editPathList').appendChild(newEditPathItem);
700
+ // 滚动到新添加的编辑器路径项
701
+ newEditPathItem.scrollIntoView({
702
+ behavior: 'smooth'
703
+ });
704
+ });
705
+
706
+ // 新增:绑定编辑器路径选择按钮事件
707
+ // document.getElementById('selectCodingEditPath').addEventListener('click', async () => {
708
+ // try {
709
+ // const res = await fetch(`${API_BASE}/select-path`, {
710
+ // method: 'POST',
711
+ // headers: { 'Content-Type': 'application/json' },
712
+ // body: JSON.stringify({ type: 'file' })
713
+ // });
714
+ // const result = await res.json();
715
+ // if (result.success && result.path) {
716
+ // document.getElementById('codingEditPath').value = result.path;
717
+ // showMsg('已选择编辑器路径');
718
+ // } else if (!result.path) {
719
+ // showMsg('未选择任何文件', false);
720
+ // }
721
+ // } catch (err) {
722
+ // showMsg('选择文件失败:' + err.message, false);
723
+ // }
724
+ // });
725
+
726
+ // 新增:绑定功能按钮事件
727
+ document.getElementById('btn-1').addEventListener('click', () => executeCommand(1));
728
+ document.getElementById('btn-2').addEventListener('click', () => executeCommand(2));
729
+ document.getElementById('btn-3').addEventListener('click', () => executeCommand(3));
730
+ document.getElementById('btn-4').addEventListener('click', () => executeCommand(4));
731
+ document.getElementById('btn-5').addEventListener('click', () => executeCommand(5));
732
+
733
+ // 新增:绑定清空终端日志事件
734
+ clearTerminalBtn.addEventListener('click', () => {
735
+ terminalLog.innerHTML = '';
736
+ logToTerminal('日志已清空', 'info');
737
+ });
738
+ </script>
739
+ </body>
740
+
741
+ </html>