添加后台进程修复等功能
This commit is contained in:
+73
-53
@@ -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("${escapeHtml(scriptName)}")">
|
onclick="selectScript("${escapeHtml(scriptName)}")"> <!-- 使用"转义双引号 -->
|
||||||
<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>
|
||||||
Reference in New Issue
Block a user