添加后台进程修复等功能

This commit is contained in:
z66
2025-08-26 18:01:12 +08:00
parent 37c42e17da
commit f0039bcbd4
+73 -53
View File
@@ -347,7 +347,6 @@
let currentSearchKeyword = ""; let currentSearchKeyword = "";
let currentServerDirectory = ""; let currentServerDirectory = "";
let selectedScriptPath = null; let selectedScriptPath = null;
let scriptStatusMap = {}; // 缓存脚本状态映射
$(function () { $(function () {
loadScripts(); loadScripts();
@@ -370,15 +369,12 @@
alert('加载脚本失败:后端返回非数组格式'); alert('加载脚本失败:后端返回非数组格式');
return; return;
} }
// 只保留有配置的脚本(后端已过滤,前端二次确认) // 直接保存原始名称,不做额外处理
allScripts = scripts.filter(item => allScripts = scripts.filter(item =>
typeof item === 'object' && item !== null && typeof item === 'object' && item !== null &&
'name' in item && typeof item.name === 'string' && item.name.trim() !== '' 'name' in item && typeof item.name === 'string' && item.name.trim() !== ''
); );
// 批量获取所有脚本的状态和模式
$.get('/api/scripts/status', function(statusMap) {
scriptStatusMap = statusMap;
currentSearchKeyword = ""; currentSearchKeyword = "";
$('#script-search').val(""); $('#script-search').val("");
sortScripts(); sortScripts();
@@ -395,10 +391,6 @@
$('#output-area').empty(); $('#output-area').empty();
} }
} }
}).fail(function(xhr) {
alert('加载脚本状态失败:' + (xhr.responseJSON?.error || xhr.statusText));
});
}).fail(function (xhr) { }).fail(function (xhr) {
alert('加载脚本列表失败:' + (xhr.responseJSON?.error || xhr.statusText)); alert('加载脚本列表失败:' + (xhr.responseJSON?.error || xhr.statusText));
allScripts = []; allScripts = [];
@@ -452,7 +444,7 @@
if (filteredScripts.length === 0) { if (filteredScripts.length === 0) {
const tip = currentSearchKeyword ? const tip = currentSearchKeyword ?
`<i class="bi bi-search"></i> 未找到包含"${currentSearchKeyword}"的脚本` : `<i class="bi bi-search"></i> 未找到包含"${currentSearchKeyword}"的脚本` :
`<i class="bi bi-folder-open"></i> 暂无配置的脚本`; `<i class="bi bi-folder-open"></i> tasks目录下无脚本文件(支持.bat/.py)`;
container.append(` container.append(`
<div class="text-center text-muted py-3"> <div class="text-center text-muted py-3">
${tip} ${tip}
@@ -461,33 +453,20 @@
return; return;
} }
// 使用文档片段减少DOM重绘
const fragment = document.createDocumentFragment();
filteredScripts.forEach(scriptObj => { filteredScripts.forEach(scriptObj => {
const scriptName = scriptObj.name.trim(); const scriptName = scriptObj.name.trim();
if (!scriptName) return; if (!scriptName) return;
const modifyTimeStr = scriptObj.modify_time_str || '未知时间'; const modifyTimeStr = scriptObj.modify_time_str || '未知时间';
// 从缓存的状态映射表中获取状态和模式 getScriptStatus(scriptName, function (status) {
const statusInfo = scriptStatusMap[scriptName] || { const statusClass = status.status === 'running' ? 'status-running' :
status: 'stopped', status.status === 'scheduled' ? 'status-scheduled' : 'status-stopped';
running: false, const statusText = status.status === 'running' ? '运行中' :
scheduled: false, status.status === 'scheduled' ? '已调度' : '停止';
mode: 'single-run', const statusTextClass = `status-text ${status.status}`;
schedule_info: ''
};
// 状态样式处理
const statusClass = statusInfo.status === 'running' ? 'status-running' :
statusInfo.status === 'scheduled' ? 'status-scheduled' : 'status-stopped';
const statusText = statusInfo.status === 'running' ? '运行中' :
statusInfo.status === 'scheduled' ? '已调度' : '停止';
const statusTextClass = `status-text ${statusInfo.status}`;
// 按钮样式处理
let btnClass, btnIcon, btnText, btnClick; let btnClass, btnIcon, btnText, btnClick;
if (statusInfo.running || statusInfo.scheduled) { if (status.running || status.scheduled) {
btnClass = 'btn-danger'; btnClass = 'btn-danger';
btnIcon = 'bi-stop-fill'; btnIcon = 'bi-stop-fill';
btnText = '停止'; btnText = '停止';
@@ -499,15 +478,9 @@
btnClick = `startScript('${escapeHtml(scriptName)}')`; btnClick = `startScript('${escapeHtml(scriptName)}')`;
} }
// 模式文本处理 const scriptItemHtml = `
const modeText = statusInfo.mode === 'long-running' ? '长期运行' :
statusInfo.mode === 'interval' ? '定时执行' : '单次运行';
const scheduleInfo = statusInfo.schedule_info ? ` | ${statusInfo.schedule_info}` : '';
// 创建DOM元素并添加到文档片段
const scriptItem = $(`
<div class="script-item ${currentScript === scriptName ? 'active' : ''}" <div class="script-item ${currentScript === scriptName ? 'active' : ''}"
onclick="selectScript(&quot;${escapeHtml(scriptName)}&quot;)"> onclick="selectScript(&quot;${escapeHtml(scriptName)}&quot;)"> <!-- 使用&quot;转义双引号 -->
<div class="d-flex justify-content-between align-items-center"> <div class="d-flex justify-content-between align-items-center">
<div> <div>
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
@@ -516,10 +489,7 @@
<span class="${statusTextClass}">${statusText}</span> <span class="${statusTextClass}">${statusText}</span>
</div> </div>
<div class="mode-info text-xs mt-1"> <div class="mode-info text-xs mt-1">
<div class="d-flex justify-content-between"> <span class="text-muted">加载模式信息中...</span>
<span class="text-muted">模式:${modeText}${scheduleInfo}</span>
<span class="text-muted">最近修改:${modifyTimeStr}</span>
</div>
</div> </div>
</div> </div>
<button class="btn btn-sm ${btnClass}" <button class="btn btn-sm ${btnClass}"
@@ -528,12 +498,22 @@
</button> </button>
</div> </div>
</div> </div>
`)[0]; `;
fragment.appendChild(scriptItem); container.append(scriptItemHtml);
});
// 一次性将所有元素添加到容器 getScriptMode(scriptName, function (mode) {
container.append(fragment); const modeText = mode === 'long-running' ? '长期运行' :
mode === 'interval' ? '定时执行' : '单次运行';
const scheduleInfo = status.schedule_info ? ` | ${status.schedule_info}` : '';
$(`.script-item:has(.script-name:contains('${escapeHtml(scriptName)}')) .mode-info`).html(`
<div class="d-flex justify-content-between">
<span class="text-muted">模式:${modeText}${scheduleInfo}</span>
<span class="text-muted">最近修改:${modifyTimeStr}</span>
</div>
`);
});
});
});
} }
function updateConfigScriptSelect() { function updateConfigScriptSelect() {
@@ -545,15 +525,19 @@
typeof scriptObj.name === 'string' && scriptObj.name.trim()) { typeof scriptObj.name === 'string' && scriptObj.name.trim()) {
const scriptName = scriptObj.name.trim(); const scriptName = scriptObj.name.trim();
const escapedName = escapeHtml(scriptName); const escapedName = escapeHtml(scriptName); // 转义仅用于显示
// value使用原始名称,显示文本用转义后的值
select.append(`<option value="${scriptName}">${escapedName}</option>`); select.append(`<option value="${scriptName}">${escapedName}</option>`);
} }
}); });
if (currentScript && select.find(`option[value="${currentScript}"]`).length) { // 恢复当前选中状态(使用原始名称匹配)
if (currentScript) {
if (select.find(`option[value="${currentScript}"]`).length) {
select.val(currentScript); select.val(currentScript);
} }
} }
}
function selectScript(scriptName) { function selectScript(scriptName) {
currentScript = scriptName; currentScript = scriptName;
@@ -567,7 +551,8 @@
$.get(`/api/start/${encodeURIComponent(scriptName)}`) $.get(`/api/start/${encodeURIComponent(scriptName)}`)
.done(function (data) { .done(function (data) {
alert(data.msg); alert(data.msg);
loadScripts(); // 重新加载列表和状态 updateOutput();
renderScriptList();
}) })
.fail(function (xhr) { .fail(function (xhr) {
alert('启动失败:' + (xhr.responseJSON?.error || xhr.statusText)); alert('启动失败:' + (xhr.responseJSON?.error || xhr.statusText));
@@ -578,13 +563,35 @@
$.get(`/api/stop/${encodeURIComponent(scriptName)}`) $.get(`/api/stop/${encodeURIComponent(scriptName)}`)
.done(function (data) { .done(function (data) {
alert(data.msg); alert(data.msg);
loadScripts(); // 重新加载列表和状态 updateOutput();
renderScriptList();
}) })
.fail(function (xhr) { .fail(function (xhr) {
alert('停止失败:' + (xhr.responseJSON?.error || xhr.statusText)); alert('停止失败:' + (xhr.responseJSON?.error || xhr.statusText));
}); });
} }
function getScriptStatus(scriptName, callback) {
$.get(`/api/status/${encodeURIComponent(scriptName)}`)
.done(function (data) {
callback(data);
})
.fail(function () {
callback({status: 'stopped', running: false, scheduled: false});
});
}
function getScriptMode(scriptName, callback) {
$.get('/api/config')
.done(function (configs) {
const mode = configs[scriptName]?.mode || 'single-run';
callback(mode);
})
.fail(function () {
callback('single-run');
});
}
function loadConfig(scriptName) { function loadConfig(scriptName) {
$.get('/api/config') $.get('/api/config')
.done(function (configs) { .done(function (configs) {
@@ -644,7 +651,7 @@
data: JSON.stringify({[scriptName]: config}), data: JSON.stringify({[scriptName]: config}),
success: function (data) { success: function (data) {
alert(data.msg); alert(data.msg);
loadScripts(); // 保存后刷新列表 renderScriptList();
}, },
error: function (xhr) { error: function (xhr) {
alert('保存失败:' + (xhr.responseJSON?.error || xhr.statusText)); alert('保存失败:' + (xhr.responseJSON?.error || xhr.statusText));
@@ -801,13 +808,26 @@
document.getElementById('selectScriptBtn').addEventListener('click', function () { document.getElementById('selectScriptBtn').addEventListener('click', function () {
if (selectedScriptPath) { if (selectedScriptPath) {
const scriptName = selectedScriptPath; const scriptName = selectedScriptPath;
$('#config-script').val(scriptName); const $select = $('#config-script');
// 检查下拉框中是否已有该脚本选项,若没有则手动添加
if ($select.find(`option[value="${escapeHtml(scriptName)}"]`).length === 0) {
$select.append(`<option value="${escapeHtml(scriptName)}">${escapeHtml(scriptName)}</option>`);
}
// 设置选中值并同步相关状态
$select.val(scriptName);
loadConfig(scriptName); loadConfig(scriptName);
selectScript(scriptName); selectScript(scriptName);
// 刷新脚本列表,确保所有组件同步
loadScripts();
const modal = bootstrap.Modal.getInstance(document.getElementById('directoryBrowserModal')); const modal = bootstrap.Modal.getInstance(document.getElementById('directoryBrowserModal'));
modal.hide(); modal.hide();
} }
}); });
</script> </script>
</body> </body>
</html> </html>