添加后台进程修复等功能
This commit is contained in:
+112
-92
@@ -347,7 +347,6 @@
|
||||
let currentSearchKeyword = "";
|
||||
let currentServerDirectory = "";
|
||||
let selectedScriptPath = null;
|
||||
let scriptStatusMap = {}; // 缓存脚本状态映射
|
||||
|
||||
$(function () {
|
||||
loadScripts();
|
||||
@@ -370,35 +369,28 @@
|
||||
alert('加载脚本失败:后端返回非数组格式');
|
||||
return;
|
||||
}
|
||||
// 只保留有配置的脚本(后端已过滤,前端二次确认)
|
||||
// 直接保存原始名称,不做额外处理
|
||||
allScripts = scripts.filter(item =>
|
||||
typeof item === 'object' && item !== null &&
|
||||
'name' in item && typeof item.name === 'string' && item.name.trim() !== ''
|
||||
);
|
||||
|
||||
// 批量获取所有脚本的状态和模式
|
||||
$.get('/api/scripts/status', function(statusMap) {
|
||||
scriptStatusMap = statusMap;
|
||||
currentSearchKeyword = "";
|
||||
$('#script-search').val("");
|
||||
sortScripts();
|
||||
updateConfigScriptSelect();
|
||||
currentSearchKeyword = "";
|
||||
$('#script-search').val("");
|
||||
sortScripts();
|
||||
updateConfigScriptSelect();
|
||||
|
||||
if (currentScript) {
|
||||
const scriptExists = allScripts.some(s => s.name === currentScript);
|
||||
if (scriptExists) {
|
||||
$('#config-script').val(currentScript);
|
||||
loadConfig(currentScript);
|
||||
updateOutput();
|
||||
} else {
|
||||
currentScript = null;
|
||||
$('#output-area').empty();
|
||||
}
|
||||
if (currentScript) {
|
||||
const scriptExists = allScripts.some(s => s.name === currentScript);
|
||||
if (scriptExists) {
|
||||
$('#config-script').val(currentScript);
|
||||
loadConfig(currentScript);
|
||||
updateOutput();
|
||||
} else {
|
||||
currentScript = null;
|
||||
$('#output-area').empty();
|
||||
}
|
||||
}).fail(function(xhr) {
|
||||
alert('加载脚本状态失败:' + (xhr.responseJSON?.error || xhr.statusText));
|
||||
});
|
||||
|
||||
}
|
||||
}).fail(function (xhr) {
|
||||
alert('加载脚本列表失败:' + (xhr.responseJSON?.error || xhr.statusText));
|
||||
allScripts = [];
|
||||
@@ -452,7 +444,7 @@
|
||||
if (filteredScripts.length === 0) {
|
||||
const tip = 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(`
|
||||
<div class="text-center text-muted py-3">
|
||||
${tip}
|
||||
@@ -461,79 +453,67 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用文档片段减少DOM重绘
|
||||
const fragment = document.createDocumentFragment();
|
||||
|
||||
filteredScripts.forEach(scriptObj => {
|
||||
const scriptName = scriptObj.name.trim();
|
||||
if (!scriptName) return;
|
||||
|
||||
const modifyTimeStr = scriptObj.modify_time_str || '未知时间';
|
||||
// 从缓存的状态映射表中获取状态和模式
|
||||
const statusInfo = scriptStatusMap[scriptName] || {
|
||||
status: 'stopped',
|
||||
running: false,
|
||||
scheduled: false,
|
||||
mode: 'single-run',
|
||||
schedule_info: ''
|
||||
};
|
||||
getScriptStatus(scriptName, function (status) {
|
||||
const statusClass = status.status === 'running' ? 'status-running' :
|
||||
status.status === 'scheduled' ? 'status-scheduled' : 'status-stopped';
|
||||
const statusText = status.status === 'running' ? '运行中' :
|
||||
status.status === 'scheduled' ? '已调度' : '停止';
|
||||
const statusTextClass = `status-text ${status.status}`;
|
||||
|
||||
// 状态样式处理
|
||||
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;
|
||||
if (status.running || status.scheduled) {
|
||||
btnClass = 'btn-danger';
|
||||
btnIcon = 'bi-stop-fill';
|
||||
btnText = '停止';
|
||||
btnClick = `stopScript('${escapeHtml(scriptName)}')`;
|
||||
} else {
|
||||
btnClass = 'btn-success';
|
||||
btnIcon = 'bi-play-fill';
|
||||
btnText = '启动';
|
||||
btnClick = `startScript('${escapeHtml(scriptName)}')`;
|
||||
}
|
||||
|
||||
// 按钮样式处理
|
||||
let btnClass, btnIcon, btnText, btnClick;
|
||||
if (statusInfo.running || statusInfo.scheduled) {
|
||||
btnClass = 'btn-danger';
|
||||
btnIcon = 'bi-stop-fill';
|
||||
btnText = '停止';
|
||||
btnClick = `stopScript('${escapeHtml(scriptName)}')`;
|
||||
} else {
|
||||
btnClass = 'btn-success';
|
||||
btnIcon = 'bi-play-fill';
|
||||
btnText = '启动';
|
||||
btnClick = `startScript('${escapeHtml(scriptName)}')`;
|
||||
}
|
||||
|
||||
// 模式文本处理
|
||||
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' : ''}"
|
||||
onclick="selectScript("${escapeHtml(scriptName)}")">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="status-indicator ${statusClass}"></span>
|
||||
<span class="script-name">${escapeHtml(scriptName)}</span>
|
||||
<span class="${statusTextClass}">${statusText}</span>
|
||||
</div>
|
||||
<div class="mode-info text-xs mt-1">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="text-muted">模式:${modeText}${scheduleInfo}</span>
|
||||
<span class="text-muted">最近修改:${modifyTimeStr}</span>
|
||||
const scriptItemHtml = `
|
||||
<div class="script-item ${currentScript === scriptName ? 'active' : ''}"
|
||||
onclick="selectScript("${escapeHtml(scriptName)}")"> <!-- 使用"转义双引号 -->
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="status-indicator ${statusClass}"></span>
|
||||
<span class="script-name">${escapeHtml(scriptName)}</span>
|
||||
<span class="${statusTextClass}">${statusText}</span>
|
||||
</div>
|
||||
<div class="mode-info text-xs mt-1">
|
||||
<span class="text-muted">加载模式信息中...</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-sm ${btnClass}"
|
||||
onclick="event.stopPropagation(); ${btnClick}">
|
||||
<i class="bi ${btnIcon}"></i> ${btnText}
|
||||
</button>
|
||||
</div>
|
||||
<button class="btn btn-sm ${btnClass}"
|
||||
onclick="event.stopPropagation(); ${btnClick}">
|
||||
<i class="bi ${btnIcon}"></i> ${btnText}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`)[0];
|
||||
fragment.appendChild(scriptItem);
|
||||
});
|
||||
`;
|
||||
container.append(scriptItemHtml);
|
||||
|
||||
// 一次性将所有元素添加到容器
|
||||
container.append(fragment);
|
||||
getScriptMode(scriptName, function (mode) {
|
||||
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() {
|
||||
@@ -545,13 +525,17 @@
|
||||
typeof scriptObj.name === 'string' && scriptObj.name.trim()) {
|
||||
|
||||
const scriptName = scriptObj.name.trim();
|
||||
const escapedName = escapeHtml(scriptName);
|
||||
const escapedName = escapeHtml(scriptName); // 转义仅用于显示
|
||||
// value使用原始名称,显示文本用转义后的值
|
||||
select.append(`<option value="${scriptName}">${escapedName}</option>`);
|
||||
}
|
||||
});
|
||||
|
||||
if (currentScript && select.find(`option[value="${currentScript}"]`).length) {
|
||||
select.val(currentScript);
|
||||
// 恢复当前选中状态(使用原始名称匹配)
|
||||
if (currentScript) {
|
||||
if (select.find(`option[value="${currentScript}"]`).length) {
|
||||
select.val(currentScript);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,7 +551,8 @@
|
||||
$.get(`/api/start/${encodeURIComponent(scriptName)}`)
|
||||
.done(function (data) {
|
||||
alert(data.msg);
|
||||
loadScripts(); // 重新加载列表和状态
|
||||
updateOutput();
|
||||
renderScriptList();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
alert('启动失败:' + (xhr.responseJSON?.error || xhr.statusText));
|
||||
@@ -578,13 +563,35 @@
|
||||
$.get(`/api/stop/${encodeURIComponent(scriptName)}`)
|
||||
.done(function (data) {
|
||||
alert(data.msg);
|
||||
loadScripts(); // 重新加载列表和状态
|
||||
updateOutput();
|
||||
renderScriptList();
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
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) {
|
||||
$.get('/api/config')
|
||||
.done(function (configs) {
|
||||
@@ -644,7 +651,7 @@
|
||||
data: JSON.stringify({[scriptName]: config}),
|
||||
success: function (data) {
|
||||
alert(data.msg);
|
||||
loadScripts(); // 保存后刷新列表
|
||||
renderScriptList();
|
||||
},
|
||||
error: function (xhr) {
|
||||
alert('保存失败:' + (xhr.responseJSON?.error || xhr.statusText));
|
||||
@@ -801,13 +808,26 @@
|
||||
document.getElementById('selectScriptBtn').addEventListener('click', function () {
|
||||
if (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);
|
||||
selectScript(scriptName);
|
||||
|
||||
// 刷新脚本列表,确保所有组件同步
|
||||
loadScripts();
|
||||
|
||||
const modal = bootstrap.Modal.getInstance(document.getElementById('directoryBrowserModal'));
|
||||
modal.hide();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user