mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-09-27 19:51:45 +02:00
Add files via upload
This commit is contained in:
@@ -9138,6 +9138,77 @@ header {
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inline editable value — hover hint */
|
||||||
|
.bq-inline-editable {
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
margin: -2px -6px;
|
||||||
|
transition: background 0.15s;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
}
|
||||||
|
.bq-inline-editable:hover {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
.bq-inline-editable::after {
|
||||||
|
content: '\270E';
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.bq-inline-editable:hover::after {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline edit controls (replaces value in-place) */
|
||||||
|
.bq-inline-edit-controls {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.bq-inline-edit-controls input[type="text"],
|
||||||
|
.bq-inline-edit-controls select {
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1.5px solid var(--accent-color);
|
||||||
|
font-size: 13px;
|
||||||
|
outline: none;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
.bq-inline-edit-controls input[type="text"]:focus,
|
||||||
|
.bq-inline-edit-controls select:focus {
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 102, 255, 0.15);
|
||||||
|
}
|
||||||
|
/* Task inline edit textarea */
|
||||||
|
.batch-task-inline-edit {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.batch-task-inline-edit textarea {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 56px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
border: 1.5px solid var(--accent-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
resize: vertical;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transition: border-color 0.15s, box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
.batch-task-inline-edit textarea:focus {
|
||||||
|
box-shadow: 0 0 0 2px rgba(0, 102, 255, 0.15);
|
||||||
|
}
|
||||||
@media (max-width: 520px) {
|
@media (max-width: 520px) {
|
||||||
.bq-kv,
|
.bq-kv,
|
||||||
.bq-kv--block {
|
.bq-kv--block {
|
||||||
|
|||||||
@@ -284,6 +284,8 @@
|
|||||||
"pauseQueueConfirm": "Pause this batch queue? The current task will be stopped; remaining tasks will stay pending.",
|
"pauseQueueConfirm": "Pause this batch queue? The current task will be stopped; remaining tasks will stay pending.",
|
||||||
"deleteQueueConfirm": "Delete this batch queue? This cannot be undone.",
|
"deleteQueueConfirm": "Delete this batch queue? This cannot be undone.",
|
||||||
"deleteQueueFailed": "Failed to delete batch queue",
|
"deleteQueueFailed": "Failed to delete batch queue",
|
||||||
|
"rerunQueueConfirm": "Rerun this batch queue? All tasks will be reset to pending and re-executed.",
|
||||||
|
"rerunQueueFailed": "Failed to rerun batch queue",
|
||||||
"batchQueueTitle": "Batch task queue",
|
"batchQueueTitle": "Batch task queue",
|
||||||
"batchQueueUntitled": "Untitled queue",
|
"batchQueueUntitled": "Untitled queue",
|
||||||
"resumeExecute": "Resume",
|
"resumeExecute": "Resume",
|
||||||
@@ -1542,6 +1544,7 @@
|
|||||||
"startExecuteNow": "Run now (one round)",
|
"startExecuteNow": "Run now (one round)",
|
||||||
"startExecuteNowConfirm": "This is a Cron queue. Clicking Start will run the current round immediately instead of waiting for the next Cron time. Continue?",
|
"startExecuteNowConfirm": "This is a Cron queue. Clicking Start will run the current round immediately instead of waiting for the next Cron time. Continue?",
|
||||||
"pauseQueue": "Pause queue",
|
"pauseQueue": "Pause queue",
|
||||||
|
"rerunQueue": "Rerun",
|
||||||
"deleteQueue": "Delete queue",
|
"deleteQueue": "Delete queue",
|
||||||
"queueTitle": "Task title",
|
"queueTitle": "Task title",
|
||||||
"role": "Role",
|
"role": "Role",
|
||||||
|
|||||||
@@ -284,6 +284,8 @@
|
|||||||
"pauseQueueConfirm": "确定要暂停这个批量任务队列吗?当前正在执行的任务将被停止,后续任务将保留待执行状态。",
|
"pauseQueueConfirm": "确定要暂停这个批量任务队列吗?当前正在执行的任务将被停止,后续任务将保留待执行状态。",
|
||||||
"deleteQueueConfirm": "确定要删除这个批量任务队列吗?此操作不可恢复。",
|
"deleteQueueConfirm": "确定要删除这个批量任务队列吗?此操作不可恢复。",
|
||||||
"deleteQueueFailed": "删除批量任务队列失败",
|
"deleteQueueFailed": "删除批量任务队列失败",
|
||||||
|
"rerunQueueConfirm": "确定要重跑一轮吗?所有子任务将被重置为待执行状态并重新开始执行。",
|
||||||
|
"rerunQueueFailed": "重跑批量任务失败",
|
||||||
"batchQueueTitle": "批量任务队列",
|
"batchQueueTitle": "批量任务队列",
|
||||||
"batchQueueUntitled": "未命名队列",
|
"batchQueueUntitled": "未命名队列",
|
||||||
"resumeExecute": "继续执行",
|
"resumeExecute": "继续执行",
|
||||||
@@ -1542,6 +1544,7 @@
|
|||||||
"startExecuteNow": "立即执行一轮",
|
"startExecuteNow": "立即执行一轮",
|
||||||
"startExecuteNowConfirm": "这是 Cron 队列,点击后会立即执行当前这一轮,不会等待下次 Cron 时间。确定立即执行吗?",
|
"startExecuteNowConfirm": "这是 Cron 队列,点击后会立即执行当前这一轮,不会等待下次 Cron 时间。确定立即执行吗?",
|
||||||
"pauseQueue": "暂停队列",
|
"pauseQueue": "暂停队列",
|
||||||
|
"rerunQueue": "重跑一轮",
|
||||||
"deleteQueue": "删除队列",
|
"deleteQueue": "删除队列",
|
||||||
"queueTitle": "任务标题",
|
"queueTitle": "任务标题",
|
||||||
"role": "角色",
|
"role": "角色",
|
||||||
|
|||||||
+342
-201
@@ -1309,6 +1309,11 @@ async function showBatchQueueDetail(queueId) {
|
|||||||
: _t('batchQueueDetailModal.startExecute');
|
: _t('batchQueueDetailModal.startExecute');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const rerunBtn = document.getElementById('batch-queue-rerun-btn');
|
||||||
|
if (rerunBtn) {
|
||||||
|
// 已完成或已取消状态显示"重跑一轮"
|
||||||
|
rerunBtn.style.display = (queue.status === 'completed' || queue.status === 'cancelled') ? 'inline-block' : 'none';
|
||||||
|
}
|
||||||
if (pauseBtn) {
|
if (pauseBtn) {
|
||||||
// running状态显示"暂停队列"
|
// running状态显示"暂停队列"
|
||||||
pauseBtn.style.display = queue.status === 'running' ? 'inline-block' : 'none';
|
pauseBtn.style.display = queue.status === 'running' ? 'inline-block' : 'none';
|
||||||
@@ -1376,41 +1381,10 @@ async function showBatchQueueDetail(queueId) {
|
|||||||
${showProgressNoteInModal ? `<p class="batch-queue-detail-hero__note">${escapeHtml(pres.progressNote)}</p>` : ''}
|
${showProgressNoteInModal ? `<p class="batch-queue-detail-hero__note">${escapeHtml(pres.progressNote)}</p>` : ''}
|
||||||
</section>
|
</section>
|
||||||
<section class="batch-queue-detail-kv">
|
<section class="batch-queue-detail-kv">
|
||||||
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.queueTitle'))}</span><span class="bq-kv__v">${escapeHtml(queue.title || _t('tasks.batchQueueUntitled'))}${allowSubtaskMutation ? ` <button class="btn-secondary btn-small" onclick="showEditMetadataInline()" style="margin-left:8px;padding:1px 8px;font-size:12px;">${escapeHtml(_t('common.edit'))}</button>` : ''}</span></div>
|
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.queueTitle'))}</span><span class="bq-kv__v" id="bq-title-val">${allowSubtaskMutation ? `<span class="bq-inline-editable" onclick="startInlineEditTitle()" title="${escapeHtml(_t('common.edit'))}">${escapeHtml(queue.title || _t('tasks.batchQueueUntitled'))}</span>` : escapeHtml(queue.title || _t('tasks.batchQueueUntitled'))}</span></div>
|
||||||
<div class="bq-kv bq-kv--block" id="bq-edit-metadata-row" style="display:none;">
|
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.role'))}</span><span class="bq-kv__v" id="bq-role-val">${allowSubtaskMutation ? `<span class="bq-inline-editable" onclick="startInlineEditRole()" title="${escapeHtml(_t('common.edit'))}">${roleLineVal}</span>` : roleLineVal}</span></div>
|
||||||
<span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.editMetadata') || '编辑信息')}</span>
|
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchImportModal.agentMode'))}</span><span class="bq-kv__v" id="bq-agentmode-val">${allowSubtaskMutation ? `<span class="bq-inline-editable" onclick="startInlineEditAgentMode()" title="${escapeHtml(_t('common.edit'))}">${escapeHtml(agentModeText)}</span>` : escapeHtml(agentModeText)}</span></div>
|
||||||
<span class="bq-kv__v bq-kv__v--control">
|
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchImportModal.scheduleMode'))}</span><span class="bq-kv__v" id="bq-schedule-val">${allowSubtaskMutation ? `<span class="bq-inline-editable" onclick="startInlineEditSchedule()" title="${escapeHtml(_t('common.edit'))}">${scheduleDetail}</span>` : scheduleDetail}</span></div>
|
||||||
<label style="font-size:12px;margin-right:4px;">${escapeHtml(_t('batchQueueDetailModal.queueTitle'))}</label>
|
|
||||||
<input type="text" id="bq-edit-title" value="${escapeHtml(queue.title || '')}" placeholder="${escapeHtml(_t('batchImportModal.queueTitleHint') || '')}" style="padding:4px 8px;border-radius:4px;border:1px solid #d0d0d0;font-size:13px;width:160px;" />
|
|
||||||
<label style="font-size:12px;margin-left:12px;margin-right:4px;">${escapeHtml(_t('batchQueueDetailModal.role'))}</label>
|
|
||||||
<select id="bq-edit-role" style="padding:4px 8px;border-radius:4px;border:1px solid #d0d0d0;font-size:13px;min-width:120px;max-width:200px;">
|
|
||||||
<option value="">${escapeHtml(_t('batchImportModal.defaultRole'))}</option>
|
|
||||||
${(() => {
|
|
||||||
const roles = (Array.isArray(loadedRoles) ? loadedRoles : []).filter(r => r.name !== '默认' && r.enabled !== false).sort((a, b) => (a.name || '').localeCompare(b.name || '', 'zh-CN'));
|
|
||||||
const currentInList = !queue.role || queue.role === '' || roles.some(r => r.name === queue.role);
|
|
||||||
const orphan = !currentInList ? `<option value="${escapeHtml(queue.role)}" selected>${escapeHtml(queue.role)} (${escapeHtml(_t('batchQueueDetailModal.roleNotFound') || '已移除')})</option>` : '';
|
|
||||||
return orphan + roles.map(r => `<option value="${escapeHtml(r.name)}" ${r.name === (queue.role || '') ? 'selected' : ''}>${escapeHtml(r.name)}</option>`).join('');
|
|
||||||
})()}
|
|
||||||
</select>
|
|
||||||
<button class="btn-primary btn-small" onclick="saveEditMetadata()" style="margin-left:8px;padding:2px 12px;font-size:12px;">${escapeHtml(_t('common.save'))}</button>
|
|
||||||
<button class="btn-secondary btn-small" onclick="hideEditMetadataInline()" style="margin-left:4px;padding:2px 12px;font-size:12px;">${escapeHtml(_t('common.cancel'))}</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.role'))}</span><span class="bq-kv__v">${roleLineVal}</span></div>
|
|
||||||
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchImportModal.agentMode'))}</span><span class="bq-kv__v">${escapeHtml(agentModeText)}</span></div>
|
|
||||||
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchImportModal.scheduleMode'))}</span><span class="bq-kv__v">${scheduleDetail}${allowSubtaskMutation ? ` <button class="btn-secondary btn-small" onclick="showEditScheduleInline()" style="margin-left:8px;padding:1px 8px;font-size:12px;">${escapeHtml(_t('batchQueueDetailModal.editSchedule'))}</button>` : ''}</span></div>
|
|
||||||
<div class="bq-kv bq-kv--block" id="bq-edit-schedule-row" style="display:none;">
|
|
||||||
<span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.editScheduleTitle'))}</span>
|
|
||||||
<span class="bq-kv__v bq-kv__v--control">
|
|
||||||
<select id="bq-edit-schedule-mode" onchange="toggleEditScheduleCronInput()" style="padding:4px 8px;border-radius:4px;border:1px solid #d0d0d0;font-size:13px;">
|
|
||||||
<option value="manual" ${queue.scheduleMode !== 'cron' ? 'selected' : ''}>${escapeHtml(_t('batchImportModal.scheduleModeManual'))}</option>
|
|
||||||
<option value="cron" ${queue.scheduleMode === 'cron' ? 'selected' : ''}>${escapeHtml(_t('batchImportModal.scheduleModeCron'))}</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" id="bq-edit-cron-expr" value="${escapeHtml(queue.cronExpr || '')}" placeholder="${_t('batchImportModal.cronExprPlaceholder', { interpolation: { escapeValue: false } })}" style="margin-left:8px;padding:4px 8px;border-radius:4px;border:1px solid #d0d0d0;font-size:13px;width:220px;${queue.scheduleMode !== 'cron' ? 'display:none;' : ''}" />
|
|
||||||
<button class="btn-primary btn-small" onclick="saveEditSchedule()" style="margin-left:8px;padding:2px 12px;font-size:12px;">${escapeHtml(_t('common.save'))}</button>
|
|
||||||
<button class="btn-secondary btn-small" onclick="hideEditScheduleInline()" style="margin-left:4px;padding:2px 12px;font-size:12px;">${escapeHtml(_t('common.cancel'))}</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.taskTotal'))}</span><span class="bq-kv__v">${queue.tasks.length}</span></div>
|
<div class="bq-kv"><span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.taskTotal'))}</span><span class="bq-kv__v">${queue.tasks.length}</span></div>
|
||||||
${queue.scheduleMode === 'cron' ? `<div class="bq-kv bq-kv--block"><span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.scheduleCronAuto'))}</span><span class="bq-kv__v bq-kv__v--control"><label class="bq-cron-toggle"><input type="checkbox" ${queue.scheduleEnabled !== false ? 'checked' : ''} onchange="updateBatchQueueScheduleEnabled(this.checked)" /><span class="bq-cron-toggle__hint">${escapeHtml(_t('batchQueueDetailModal.scheduleCronAutoHint'))}</span></label></span></div>` : ''}
|
${queue.scheduleMode === 'cron' ? `<div class="bq-kv bq-kv--block"><span class="bq-kv__k">${escapeHtml(_t('batchQueueDetailModal.scheduleCronAuto'))}</span><span class="bq-kv__v bq-kv__v--control"><label class="bq-cron-toggle"><input type="checkbox" ${queue.scheduleEnabled !== false ? 'checked' : ''} onchange="updateBatchQueueScheduleEnabled(this.checked)" /><span class="bq-cron-toggle__hint">${escapeHtml(_t('batchQueueDetailModal.scheduleCronAutoHint'))}</span></label></span></div>` : ''}
|
||||||
</section>
|
</section>
|
||||||
@@ -1555,6 +1529,36 @@ async function pauseBatchQueue() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 重跑批量任务队列
|
||||||
|
async function rerunBatchQueue() {
|
||||||
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
|
if (!queueId) return;
|
||||||
|
|
||||||
|
if (!confirm(_t('tasks.rerunQueueConfirm'))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const btn = document.getElementById('batch-queue-rerun-btn');
|
||||||
|
if (btn) { btn.disabled = true; }
|
||||||
|
try {
|
||||||
|
const response = await apiFetch(`/api/batch-tasks/${queueId}/rerun`, {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const result = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(result.error || _t('tasks.rerunQueueFailed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
showBatchQueueDetail(queueId);
|
||||||
|
refreshBatchQueues();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('重跑批量任务失败:', error);
|
||||||
|
alert(_t('tasks.rerunQueueFailed') + ': ' + error.message);
|
||||||
|
} finally {
|
||||||
|
if (btn) { btn.disabled = false; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 删除批量任务队列(从详情模态框)
|
// 删除批量任务队列(从详情模态框)
|
||||||
async function deleteBatchQueue() {
|
async function deleteBatchQueue() {
|
||||||
const queueId = batchQueuesState.currentQueueId;
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
@@ -1633,15 +1637,14 @@ function startBatchQueueRefresh(queueId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
batchQueuesState.refreshInterval = setInterval(() => {
|
batchQueuesState.refreshInterval = setInterval(() => {
|
||||||
// 如果编辑或添加任务的模态框正在打开,跳过本次刷新防止丢失编辑内容
|
// 如果有内联编辑或添加任务模态框正在打开,跳过本次刷新防止丢失编辑内容
|
||||||
const editModal = document.getElementById('edit-batch-task-modal');
|
|
||||||
const addModal = document.getElementById('add-batch-task-modal');
|
const addModal = document.getElementById('add-batch-task-modal');
|
||||||
const editScheduleRow = document.getElementById('bq-edit-schedule-row');
|
const content = document.getElementById('batch-queue-detail-content');
|
||||||
const editMetadataRow = document.getElementById('bq-edit-metadata-row');
|
const hasInlineEdit = content && (
|
||||||
if ((editModal && editModal.style.display === 'block') ||
|
content.querySelector('.bq-inline-edit-controls') ||
|
||||||
(addModal && addModal.style.display === 'block') ||
|
content.querySelector('.batch-task-inline-edit')
|
||||||
(editScheduleRow && editScheduleRow.style.display !== 'none') ||
|
);
|
||||||
(editMetadataRow && editMetadataRow.style.display !== 'none')) {
|
if ((addModal && addModal.style.display === 'block') || hasInlineEdit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (batchQueuesState.currentQueueId === queueId) {
|
if (batchQueuesState.currentQueueId === queueId) {
|
||||||
@@ -1678,157 +1681,96 @@ function viewBatchTaskConversation(conversationId) {
|
|||||||
window.location.hash = `chat?conversation=${conversationId}`;
|
window.location.hash = `chat?conversation=${conversationId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑批量任务的状态
|
// --- 内联编辑:任务消息 ---
|
||||||
const editBatchTaskState = {
|
// 从元素获取任务信息并启动内联编辑
|
||||||
queueId: null,
|
|
||||||
taskId: null,
|
|
||||||
_escHandler: null,
|
|
||||||
_saveHandler: null
|
|
||||||
};
|
|
||||||
|
|
||||||
// 从元素获取任务信息并打开编辑模态框
|
|
||||||
function editBatchTaskFromElement(button) {
|
function editBatchTaskFromElement(button) {
|
||||||
const taskItem = button.closest('.batch-task-item');
|
const taskItem = button.closest('.batch-task-item');
|
||||||
if (!taskItem) {
|
if (!taskItem) return;
|
||||||
console.error('无法找到任务项元素');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const queueId = taskItem.getAttribute('data-queue-id');
|
const queueId = taskItem.getAttribute('data-queue-id');
|
||||||
const taskId = taskItem.getAttribute('data-task-id');
|
const taskId = taskItem.getAttribute('data-task-id');
|
||||||
const taskMessage = taskItem.getAttribute('data-task-message');
|
const taskMessage = taskItem.getAttribute('data-task-message');
|
||||||
|
if (!queueId || !taskId) return;
|
||||||
if (!queueId || !taskId) {
|
|
||||||
console.error('任务信息不完整');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解码HTML实体
|
// 解码HTML实体
|
||||||
const decodedMessage = taskMessage
|
const decodedMessage = taskMessage
|
||||||
.replace(/'/g, "'")
|
.replace(/'/g, "'")
|
||||||
.replace(/"/g, '"')
|
.replace(/"/g, '"')
|
||||||
.replace(/\\n/g, '\n');
|
.replace(/\\n/g, '\n');
|
||||||
|
|
||||||
editBatchTask(queueId, taskId, decodedMessage);
|
// 找到 .batch-task-message 和 header 中的按钮
|
||||||
|
const msgSpan = taskItem.querySelector('.batch-task-message');
|
||||||
|
const header = taskItem.querySelector('.batch-task-header');
|
||||||
|
if (!msgSpan || !header) return;
|
||||||
|
|
||||||
|
// 隐藏编辑/删除按钮
|
||||||
|
header.querySelectorAll('.batch-task-edit-btn, .batch-task-delete-btn').forEach(b => b.style.display = 'none');
|
||||||
|
|
||||||
|
// 替换消息为内联编辑区域
|
||||||
|
const editDiv = document.createElement('div');
|
||||||
|
editDiv.className = 'batch-task-inline-edit';
|
||||||
|
editDiv.innerHTML = `<textarea id="bq-task-edit-${escapeHtml(taskId)}">${escapeHtml(decodedMessage)}</textarea>`;
|
||||||
|
msgSpan.style.display = 'none';
|
||||||
|
msgSpan.parentNode.insertBefore(editDiv, msgSpan.nextSibling);
|
||||||
|
|
||||||
|
const textarea = editDiv.querySelector('textarea');
|
||||||
|
if (textarea) {
|
||||||
|
let taskCancelled = false;
|
||||||
|
textarea.focus();
|
||||||
|
textarea.setSelectionRange(textarea.value.length, textarea.value.length);
|
||||||
|
textarea.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
taskCancelled = true;
|
||||||
|
cancelInlineTask();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
textarea.addEventListener('blur', () => {
|
||||||
|
if (!taskCancelled) saveInlineTask(queueId, taskId);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开编辑批量任务模态框
|
function cancelInlineTask() {
|
||||||
function editBatchTask(queueId, taskId, currentMessage) {
|
// 刷新整个详情来还原
|
||||||
editBatchTaskState.queueId = queueId;
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
editBatchTaskState.taskId = taskId;
|
if (queueId) showBatchQueueDetail(queueId);
|
||||||
|
|
||||||
const modal = document.getElementById('edit-batch-task-modal');
|
|
||||||
const messageInput = document.getElementById('edit-task-message');
|
|
||||||
|
|
||||||
if (!modal || !messageInput) {
|
|
||||||
console.error('编辑任务模态框元素不存在');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
messageInput.value = currentMessage;
|
|
||||||
modal.style.display = 'block';
|
|
||||||
|
|
||||||
// 聚焦到输入框
|
|
||||||
setTimeout(() => {
|
|
||||||
messageInput.focus();
|
|
||||||
messageInput.select();
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
// 清理旧的事件监听器(防止泄漏)
|
|
||||||
if (editBatchTaskState._escHandler) {
|
|
||||||
document.removeEventListener('keydown', editBatchTaskState._escHandler);
|
|
||||||
}
|
|
||||||
if (editBatchTaskState._saveHandler) {
|
|
||||||
messageInput.removeEventListener('keydown', editBatchTaskState._saveHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加ESC键监听
|
|
||||||
editBatchTaskState._escHandler = (e) => {
|
|
||||||
if (e.key === 'Escape') {
|
|
||||||
closeEditBatchTaskModal();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
document.addEventListener('keydown', editBatchTaskState._escHandler);
|
|
||||||
|
|
||||||
// 添加Enter+Ctrl/Cmd保存功能
|
|
||||||
editBatchTaskState._saveHandler = (e) => {
|
|
||||||
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
|
||||||
e.preventDefault();
|
|
||||||
saveBatchTask();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
messageInput.addEventListener('keydown', editBatchTaskState._saveHandler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭编辑批量任务模态框
|
async function saveInlineTask(queueId, taskId) {
|
||||||
function closeEditBatchTaskModal() {
|
if (_bqInlineSaving) return;
|
||||||
const modal = document.getElementById('edit-batch-task-modal');
|
_bqInlineSaving = true;
|
||||||
if (modal) {
|
const textarea = document.getElementById(`bq-task-edit-${taskId}`);
|
||||||
modal.style.display = 'none';
|
if (!textarea) { _bqInlineSaving = false; return; }
|
||||||
}
|
|
||||||
// 清理事件监听器
|
|
||||||
if (editBatchTaskState._escHandler) {
|
|
||||||
document.removeEventListener('keydown', editBatchTaskState._escHandler);
|
|
||||||
editBatchTaskState._escHandler = null;
|
|
||||||
}
|
|
||||||
if (editBatchTaskState._saveHandler) {
|
|
||||||
const messageInput = document.getElementById('edit-task-message');
|
|
||||||
if (messageInput) {
|
|
||||||
messageInput.removeEventListener('keydown', editBatchTaskState._saveHandler);
|
|
||||||
}
|
|
||||||
editBatchTaskState._saveHandler = null;
|
|
||||||
}
|
|
||||||
editBatchTaskState.queueId = null;
|
|
||||||
editBatchTaskState.taskId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存批量任务
|
const message = textarea.value.trim();
|
||||||
async function saveBatchTask() {
|
|
||||||
const queueId = editBatchTaskState.queueId;
|
|
||||||
const taskId = editBatchTaskState.taskId;
|
|
||||||
const messageInput = document.getElementById('edit-task-message');
|
|
||||||
|
|
||||||
if (!queueId || !taskId) {
|
|
||||||
alert(_t('tasks.taskIncomplete'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!messageInput) {
|
|
||||||
alert(_t('tasks.cannotGetTaskMessageInput'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = messageInput.value.trim();
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
|
_bqInlineSaving = false;
|
||||||
alert(_t('tasks.taskMessageRequired'));
|
alert(_t('tasks.taskMessageRequired'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await apiFetch(`/api/batch-tasks/${queueId}/tasks/${taskId}`, {
|
const response = await apiFetch(`/api/batch-tasks/${queueId}/tasks/${taskId}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'Content-Type': 'application/json',
|
body: JSON.stringify({ message }),
|
||||||
},
|
|
||||||
body: JSON.stringify({ message: message }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const result = await response.json().catch(() => ({}));
|
const result = await response.json().catch(() => ({}));
|
||||||
throw new Error(result.error || _t('tasks.updateTaskFailed'));
|
throw new Error(result.error || _t('tasks.updateTaskFailed'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 关闭编辑模态框
|
_bqInlineSaving = false;
|
||||||
closeEditBatchTaskModal();
|
|
||||||
|
|
||||||
// 刷新队列详情
|
// 刷新队列详情
|
||||||
if (batchQueuesState.currentQueueId === queueId) {
|
if (batchQueuesState.currentQueueId === queueId) {
|
||||||
showBatchQueueDetail(queueId);
|
showBatchQueueDetail(queueId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新队列列表
|
// 刷新队列列表
|
||||||
refreshBatchQueues();
|
refreshBatchQueues();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
_bqInlineSaving = false;
|
||||||
console.error('保存任务失败:', error);
|
console.error('保存任务失败:', error);
|
||||||
alert(_t('tasks.saveTaskFailed') + ': ' + error.message);
|
alert(_t('tasks.saveTaskFailed') + ': ' + error.message);
|
||||||
}
|
}
|
||||||
@@ -2046,26 +1988,54 @@ async function updateBatchQueueScheduleEnabled(enabled) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 元数据(标题/角色)内联编辑 ---
|
// --- 内联编辑:取消所有正在编辑的内联区域 ---
|
||||||
function showEditMetadataInline() {
|
function cancelAllInlineEdits() {
|
||||||
// 关闭调度编辑行(互斥)
|
_bqInlineSaving = true; // 防止 blur 触发保存
|
||||||
const schedRow = document.getElementById('bq-edit-schedule-row');
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
if (schedRow) schedRow.style.display = 'none';
|
if (queueId) showBatchQueueDetail(queueId);
|
||||||
const row = document.getElementById('bq-edit-metadata-row');
|
_bqInlineSaving = false;
|
||||||
if (row) row.style.display = '';
|
|
||||||
}
|
}
|
||||||
function hideEditMetadataInline() {
|
|
||||||
const row = document.getElementById('bq-edit-metadata-row');
|
// --- 内联编辑:标题 ---
|
||||||
if (row) row.style.display = 'none';
|
let _bqInlineSaving = false;
|
||||||
}
|
function startInlineEditTitle() {
|
||||||
async function saveEditMetadata() {
|
const container = document.getElementById('bq-title-val');
|
||||||
|
if (!container) return;
|
||||||
const queueId = batchQueuesState.currentQueueId;
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
if (!queueId) return;
|
if (!queueId) return;
|
||||||
const titleInput = document.getElementById('bq-edit-title');
|
const currentTitle = (container.querySelector('.bq-inline-editable') || container).textContent.trim();
|
||||||
const roleInput = document.getElementById('bq-edit-role');
|
const untitledText = _t('tasks.batchQueueUntitled');
|
||||||
const title = titleInput ? titleInput.value.trim() : '';
|
const val = currentTitle === untitledText ? '' : currentTitle;
|
||||||
const role = roleInput ? roleInput.value.trim() : '';
|
container.innerHTML = `<span class="bq-inline-edit-controls">
|
||||||
|
<input type="text" id="bq-edit-title" value="${escapeHtml(val)}" placeholder="${escapeHtml(_t('batchImportModal.queueTitleHint') || '')}" style="width:180px;" />
|
||||||
|
</span>`;
|
||||||
|
const inp = document.getElementById('bq-edit-title');
|
||||||
|
if (inp) {
|
||||||
|
inp.focus();
|
||||||
|
inp.select();
|
||||||
|
let cancelled = false;
|
||||||
|
inp.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') { e.preventDefault(); inp.blur(); }
|
||||||
|
if (e.key === 'Escape') { cancelled = true; cancelAllInlineEdits(); }
|
||||||
|
});
|
||||||
|
inp.addEventListener('blur', () => {
|
||||||
|
if (cancelled) return;
|
||||||
|
saveInlineTitle();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function saveInlineTitle() {
|
||||||
|
if (_bqInlineSaving) return;
|
||||||
|
_bqInlineSaving = true;
|
||||||
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
|
if (!queueId) { _bqInlineSaving = false; return; }
|
||||||
|
const inp = document.getElementById('bq-edit-title');
|
||||||
|
const title = inp ? inp.value.trim() : '';
|
||||||
try {
|
try {
|
||||||
|
// 获取当前角色(保持不变)
|
||||||
|
const detailResp = await apiFetch(`/api/batch-tasks/${queueId}`);
|
||||||
|
const detail = await detailResp.json();
|
||||||
|
const role = detail.queue ? (detail.queue.role || '') : '';
|
||||||
const response = await apiFetch(`/api/batch-tasks/${queueId}/metadata`, {
|
const response = await apiFetch(`/api/batch-tasks/${queueId}/metadata`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -2075,9 +2045,131 @@ async function saveEditMetadata() {
|
|||||||
const result = await response.json().catch(() => ({}));
|
const result = await response.json().catch(() => ({}));
|
||||||
throw new Error(result.error || _t('tasks.updateTaskFailed'));
|
throw new Error(result.error || _t('tasks.updateTaskFailed'));
|
||||||
}
|
}
|
||||||
|
_bqInlineSaving = false;
|
||||||
showBatchQueueDetail(queueId);
|
showBatchQueueDetail(queueId);
|
||||||
refreshBatchQueues();
|
refreshBatchQueues();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
_bqInlineSaving = false;
|
||||||
|
console.error(e);
|
||||||
|
alert(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 内联编辑:角色 ---
|
||||||
|
function startInlineEditRole() {
|
||||||
|
const container = document.getElementById('bq-role-val');
|
||||||
|
if (!container) return;
|
||||||
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
|
if (!queueId) return;
|
||||||
|
// 获取当前详情中角色名 — 从 layout 的 data 中无法获取,故使用 API 拉取
|
||||||
|
apiFetch(`/api/batch-tasks/${queueId}`).then(r => r.json()).then(detail => {
|
||||||
|
const queue = detail.queue;
|
||||||
|
const currentRole = queue.role || '';
|
||||||
|
const roles = (Array.isArray(batchQueuesState.loadedRoles) ? batchQueuesState.loadedRoles : []).filter(r => r.name !== '默认' && r.enabled !== false).sort((a, b) => (a.name || '').localeCompare(b.name || '', 'zh-CN'));
|
||||||
|
const currentInList = !currentRole || roles.some(r => r.name === currentRole);
|
||||||
|
const orphanOpt = !currentInList ? `<option value="${escapeHtml(currentRole)}" selected>${escapeHtml(currentRole)} (${escapeHtml(_t('batchQueueDetailModal.roleNotFound') || '已移除')})</option>` : '';
|
||||||
|
const opts = roles.map(r => `<option value="${escapeHtml(r.name)}" ${r.name === currentRole ? 'selected' : ''}>${escapeHtml(r.name)}</option>`).join('');
|
||||||
|
container.innerHTML = `<span class="bq-inline-edit-controls">
|
||||||
|
<select id="bq-edit-role">
|
||||||
|
<option value="">${escapeHtml(_t('batchImportModal.defaultRole'))}</option>
|
||||||
|
${orphanOpt}${opts}
|
||||||
|
</select>
|
||||||
|
</span>`;
|
||||||
|
const sel = document.getElementById('bq-edit-role');
|
||||||
|
if (sel) {
|
||||||
|
sel.focus();
|
||||||
|
let cancelled = false;
|
||||||
|
sel.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') { cancelled = true; cancelAllInlineEdits(); }
|
||||||
|
});
|
||||||
|
sel.addEventListener('change', () => { if (!cancelled) saveInlineRole(); });
|
||||||
|
sel.addEventListener('blur', () => { if (!cancelled) saveInlineRole(); });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function saveInlineRole() {
|
||||||
|
if (_bqInlineSaving) return;
|
||||||
|
_bqInlineSaving = true;
|
||||||
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
|
if (!queueId) { _bqInlineSaving = false; return; }
|
||||||
|
const sel = document.getElementById('bq-edit-role');
|
||||||
|
const role = sel ? sel.value.trim() : '';
|
||||||
|
try {
|
||||||
|
const detailResp = await apiFetch(`/api/batch-tasks/${queueId}`);
|
||||||
|
const detail = await detailResp.json();
|
||||||
|
const title = detail.queue ? (detail.queue.title || '') : '';
|
||||||
|
const response = await apiFetch(`/api/batch-tasks/${queueId}/metadata`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ title, role }),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const result = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(result.error || _t('tasks.updateTaskFailed'));
|
||||||
|
}
|
||||||
|
_bqInlineSaving = false;
|
||||||
|
showBatchQueueDetail(queueId);
|
||||||
|
refreshBatchQueues();
|
||||||
|
} catch (e) {
|
||||||
|
_bqInlineSaving = false;
|
||||||
|
console.error(e);
|
||||||
|
alert(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 内联编辑:代理模式 ---
|
||||||
|
function startInlineEditAgentMode() {
|
||||||
|
const container = document.getElementById('bq-agentmode-val');
|
||||||
|
if (!container) return;
|
||||||
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
|
if (!queueId) return;
|
||||||
|
apiFetch(`/api/batch-tasks/${queueId}`).then(r => r.json()).then(detail => {
|
||||||
|
const queue = detail.queue;
|
||||||
|
const currentMode = queue.agentMode || 'single';
|
||||||
|
container.innerHTML = `<span class="bq-inline-edit-controls">
|
||||||
|
<select id="bq-edit-agentmode">
|
||||||
|
<option value="single" ${currentMode !== 'multi' ? 'selected' : ''}>${escapeHtml(_t('batchImportModal.agentModeSingle'))}</option>
|
||||||
|
<option value="multi" ${currentMode === 'multi' ? 'selected' : ''}>${escapeHtml(_t('batchImportModal.agentModeMulti'))}</option>
|
||||||
|
</select>
|
||||||
|
</span>`;
|
||||||
|
const sel = document.getElementById('bq-edit-agentmode');
|
||||||
|
if (sel) {
|
||||||
|
sel.focus();
|
||||||
|
let cancelled = false;
|
||||||
|
sel.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') { cancelled = true; cancelAllInlineEdits(); }
|
||||||
|
});
|
||||||
|
sel.addEventListener('change', () => { if (!cancelled) saveInlineAgentMode(); });
|
||||||
|
sel.addEventListener('blur', () => { if (!cancelled) saveInlineAgentMode(); });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async function saveInlineAgentMode() {
|
||||||
|
if (_bqInlineSaving) return;
|
||||||
|
_bqInlineSaving = true;
|
||||||
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
|
if (!queueId) { _bqInlineSaving = false; return; }
|
||||||
|
const sel = document.getElementById('bq-edit-agentmode');
|
||||||
|
const agentMode = sel ? sel.value : 'single';
|
||||||
|
try {
|
||||||
|
const detailResp = await apiFetch(`/api/batch-tasks/${queueId}`);
|
||||||
|
const detail = await detailResp.json();
|
||||||
|
const title = detail.queue ? (detail.queue.title || '') : '';
|
||||||
|
const role = detail.queue ? (detail.queue.role || '') : '';
|
||||||
|
const response = await apiFetch(`/api/batch-tasks/${queueId}/metadata`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ title, role, agentMode }),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const result = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(result.error || _t('tasks.updateTaskFailed'));
|
||||||
|
}
|
||||||
|
_bqInlineSaving = false;
|
||||||
|
showBatchQueueDetail(queueId);
|
||||||
|
refreshBatchQueues();
|
||||||
|
} catch (e) {
|
||||||
|
_bqInlineSaving = false;
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert(e.message);
|
alert(e.message);
|
||||||
}
|
}
|
||||||
@@ -2119,38 +2211,82 @@ async function retryBatchTask(queueId, taskId) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 调度配置内联编辑 ---
|
// --- 内联编辑:调度配置 ---
|
||||||
function showEditScheduleInline() {
|
function startInlineEditSchedule() {
|
||||||
// 关闭元数据编辑行(互斥)
|
const container = document.getElementById('bq-schedule-val');
|
||||||
const metaRow = document.getElementById('bq-edit-metadata-row');
|
if (!container) return;
|
||||||
if (metaRow) metaRow.style.display = 'none';
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
const row = document.getElementById('bq-edit-schedule-row');
|
if (!queueId) return;
|
||||||
if (row) row.style.display = '';
|
apiFetch(`/api/batch-tasks/${queueId}`).then(r => r.json()).then(detail => {
|
||||||
|
const queue = detail.queue;
|
||||||
|
const isCron = queue.scheduleMode === 'cron';
|
||||||
|
container.innerHTML = `<span class="bq-inline-edit-controls">
|
||||||
|
<select id="bq-edit-schedule-mode" onchange="toggleInlineScheduleCron()">
|
||||||
|
<option value="manual" ${!isCron ? 'selected' : ''}>${escapeHtml(_t('batchImportModal.scheduleModeManual'))}</option>
|
||||||
|
<option value="cron" ${isCron ? 'selected' : ''}>${escapeHtml(_t('batchImportModal.scheduleModeCron'))}</option>
|
||||||
|
</select>
|
||||||
|
<input type="text" id="bq-edit-cron-expr" value="${escapeHtml(queue.cronExpr || '')}" placeholder="${_t('batchImportModal.cronExprPlaceholder', { interpolation: { escapeValue: false } })}" style="width:200px;${!isCron ? 'display:none;' : ''}" />
|
||||||
|
</span>`;
|
||||||
|
let schedCancelled = false;
|
||||||
|
const sel = document.getElementById('bq-edit-schedule-mode');
|
||||||
|
const cronInp = document.getElementById('bq-edit-cron-expr');
|
||||||
|
if (sel) {
|
||||||
|
sel.focus();
|
||||||
|
sel.addEventListener('keydown', (e) => { if (e.key === 'Escape') { schedCancelled = true; cancelAllInlineEdits(); } });
|
||||||
|
sel.addEventListener('change', () => {
|
||||||
|
toggleInlineScheduleCron();
|
||||||
|
// 切到 manual 时直接保存;切到 cron 时等用户输入表达式后 blur 保存
|
||||||
|
if (sel.value !== 'cron' && !schedCancelled) saveInlineSchedule();
|
||||||
|
});
|
||||||
|
sel.addEventListener('blur', (e) => {
|
||||||
|
// 如果焦点移到了 cron 输入框,不触发保存
|
||||||
|
setTimeout(() => {
|
||||||
|
const active = document.activeElement;
|
||||||
|
if (active && (active.id === 'bq-edit-cron-expr' || active.id === 'bq-edit-schedule-mode')) return;
|
||||||
|
if (!schedCancelled) saveInlineSchedule();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (cronInp) {
|
||||||
|
cronInp.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter') { e.preventDefault(); cronInp.blur(); }
|
||||||
|
if (e.key === 'Escape') { schedCancelled = true; cancelAllInlineEdits(); }
|
||||||
|
});
|
||||||
|
cronInp.addEventListener('blur', () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
const active = document.activeElement;
|
||||||
|
if (active && (active.id === 'bq-edit-cron-expr' || active.id === 'bq-edit-schedule-mode')) return;
|
||||||
|
if (!schedCancelled) saveInlineSchedule();
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function hideEditScheduleInline() {
|
function toggleInlineScheduleCron() {
|
||||||
const row = document.getElementById('bq-edit-schedule-row');
|
|
||||||
if (row) row.style.display = 'none';
|
|
||||||
}
|
|
||||||
function toggleEditScheduleCronInput() {
|
|
||||||
const modeSelect = document.getElementById('bq-edit-schedule-mode');
|
const modeSelect = document.getElementById('bq-edit-schedule-mode');
|
||||||
const cronInput = document.getElementById('bq-edit-cron-expr');
|
const cronInput = document.getElementById('bq-edit-cron-expr');
|
||||||
if (modeSelect && cronInput) {
|
if (modeSelect && cronInput) {
|
||||||
cronInput.style.display = modeSelect.value === 'cron' ? '' : 'none';
|
cronInput.style.display = modeSelect.value === 'cron' ? '' : 'none';
|
||||||
|
if (modeSelect.value === 'cron') cronInput.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function saveEditSchedule() {
|
async function saveInlineSchedule() {
|
||||||
|
if (_bqInlineSaving) return;
|
||||||
|
_bqInlineSaving = true;
|
||||||
const queueId = batchQueuesState.currentQueueId;
|
const queueId = batchQueuesState.currentQueueId;
|
||||||
if (!queueId) return;
|
if (!queueId) { _bqInlineSaving = false; return; }
|
||||||
const modeSelect = document.getElementById('bq-edit-schedule-mode');
|
const modeSelect = document.getElementById('bq-edit-schedule-mode');
|
||||||
const cronInput = document.getElementById('bq-edit-cron-expr');
|
const cronInput = document.getElementById('bq-edit-cron-expr');
|
||||||
if (!modeSelect) return;
|
if (!modeSelect) { _bqInlineSaving = false; return; }
|
||||||
const scheduleMode = modeSelect.value;
|
const scheduleMode = modeSelect.value;
|
||||||
const cronExpr = cronInput ? cronInput.value.trim() : '';
|
const cronExpr = cronInput ? cronInput.value.trim() : '';
|
||||||
if (scheduleMode === 'cron' && !cronExpr) {
|
if (scheduleMode === 'cron' && !cronExpr) {
|
||||||
|
_bqInlineSaving = false;
|
||||||
alert(_t('batchImportModal.cronExprRequired'));
|
alert(_t('batchImportModal.cronExprRequired'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (scheduleMode === 'cron' && !/^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/.test(cronExpr)) {
|
if (scheduleMode === 'cron' && !/^\S+\s+\S+\s+\S+\s+\S+\s+\S+$/.test(cronExpr)) {
|
||||||
|
_bqInlineSaving = false;
|
||||||
alert(_t('batchImportModal.cronExprInvalid') || 'Cron 表达式格式错误,需要 5 段(分 时 日 月 周)');
|
alert(_t('batchImportModal.cronExprInvalid') || 'Cron 表达式格式错误,需要 5 段(分 时 日 月 周)');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2164,9 +2300,11 @@ async function saveEditSchedule() {
|
|||||||
const result = await response.json().catch(() => ({}));
|
const result = await response.json().catch(() => ({}));
|
||||||
throw new Error(result.error || _t('batchQueueDetailModal.editScheduleError'));
|
throw new Error(result.error || _t('batchQueueDetailModal.editScheduleError'));
|
||||||
}
|
}
|
||||||
|
_bqInlineSaving = false;
|
||||||
showBatchQueueDetail(queueId);
|
showBatchQueueDetail(queueId);
|
||||||
refreshBatchQueues();
|
refreshBatchQueues();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
_bqInlineSaving = false;
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert(_t('batchQueueDetailModal.editScheduleError') + ': ' + e.message);
|
alert(_t('batchQueueDetailModal.editScheduleError') + ': ' + e.message);
|
||||||
}
|
}
|
||||||
@@ -2179,14 +2317,14 @@ window.createBatchQueue = createBatchQueue;
|
|||||||
window.showBatchQueueDetail = showBatchQueueDetail;
|
window.showBatchQueueDetail = showBatchQueueDetail;
|
||||||
window.startBatchQueue = startBatchQueue;
|
window.startBatchQueue = startBatchQueue;
|
||||||
window.pauseBatchQueue = pauseBatchQueue;
|
window.pauseBatchQueue = pauseBatchQueue;
|
||||||
|
window.rerunBatchQueue = rerunBatchQueue;
|
||||||
window.deleteBatchQueue = deleteBatchQueue;
|
window.deleteBatchQueue = deleteBatchQueue;
|
||||||
window.closeBatchQueueDetailModal = closeBatchQueueDetailModal;
|
window.closeBatchQueueDetailModal = closeBatchQueueDetailModal;
|
||||||
window.refreshBatchQueues = refreshBatchQueues;
|
window.refreshBatchQueues = refreshBatchQueues;
|
||||||
window.viewBatchTaskConversation = viewBatchTaskConversation;
|
window.viewBatchTaskConversation = viewBatchTaskConversation;
|
||||||
window.editBatchTask = editBatchTask;
|
|
||||||
window.editBatchTaskFromElement = editBatchTaskFromElement;
|
window.editBatchTaskFromElement = editBatchTaskFromElement;
|
||||||
window.closeEditBatchTaskModal = closeEditBatchTaskModal;
|
window.cancelInlineTask = cancelInlineTask;
|
||||||
window.saveBatchTask = saveBatchTask;
|
window.saveInlineTask = saveInlineTask;
|
||||||
window.filterBatchQueues = filterBatchQueues;
|
window.filterBatchQueues = filterBatchQueues;
|
||||||
window.goBatchQueuesPage = goBatchQueuesPage;
|
window.goBatchQueuesPage = goBatchQueuesPage;
|
||||||
window.changeBatchQueuesPageSize = changeBatchQueuesPageSize;
|
window.changeBatchQueuesPageSize = changeBatchQueuesPageSize;
|
||||||
@@ -2197,14 +2335,17 @@ window.deleteBatchTaskFromElement = deleteBatchTaskFromElement;
|
|||||||
window.deleteBatchQueueFromList = deleteBatchQueueFromList;
|
window.deleteBatchQueueFromList = deleteBatchQueueFromList;
|
||||||
window.handleBatchScheduleModeChange = handleBatchScheduleModeChange;
|
window.handleBatchScheduleModeChange = handleBatchScheduleModeChange;
|
||||||
window.updateBatchQueueScheduleEnabled = updateBatchQueueScheduleEnabled;
|
window.updateBatchQueueScheduleEnabled = updateBatchQueueScheduleEnabled;
|
||||||
window.showEditMetadataInline = showEditMetadataInline;
|
window.cancelAllInlineEdits = cancelAllInlineEdits;
|
||||||
window.hideEditMetadataInline = hideEditMetadataInline;
|
window.startInlineEditTitle = startInlineEditTitle;
|
||||||
window.saveEditMetadata = saveEditMetadata;
|
window.saveInlineTitle = saveInlineTitle;
|
||||||
|
window.startInlineEditRole = startInlineEditRole;
|
||||||
|
window.saveInlineRole = saveInlineRole;
|
||||||
|
window.startInlineEditAgentMode = startInlineEditAgentMode;
|
||||||
|
window.saveInlineAgentMode = saveInlineAgentMode;
|
||||||
window.retryBatchTask = retryBatchTask;
|
window.retryBatchTask = retryBatchTask;
|
||||||
window.showEditScheduleInline = showEditScheduleInline;
|
window.startInlineEditSchedule = startInlineEditSchedule;
|
||||||
window.hideEditScheduleInline = hideEditScheduleInline;
|
window.toggleInlineScheduleCron = toggleInlineScheduleCron;
|
||||||
window.toggleEditScheduleCronInput = toggleEditScheduleCronInput;
|
window.saveInlineSchedule = saveInlineSchedule;
|
||||||
window.saveEditSchedule = saveEditSchedule;
|
|
||||||
|
|
||||||
// 语言切换后,列表/分页/详情弹窗由 JS 渲染的文案需用当前语言重绘(applyTranslations 不会处理 innerHTML 内容)
|
// 语言切换后,列表/分页/详情弹窗由 JS 渲染的文案需用当前语言重绘(applyTranslations 不会处理 innerHTML 内容)
|
||||||
document.addEventListener('languagechange', function () {
|
document.addEventListener('languagechange', function () {
|
||||||
|
|||||||
@@ -2395,6 +2395,7 @@ version: 1.0.0<br>
|
|||||||
<div class="modal-header-actions">
|
<div class="modal-header-actions">
|
||||||
<button class="btn-secondary btn-small" id="batch-queue-add-task-btn" onclick="showAddBatchTaskModal()" style="display: none;" data-i18n="batchQueueDetailModal.addTask">添加任务</button>
|
<button class="btn-secondary btn-small" id="batch-queue-add-task-btn" onclick="showAddBatchTaskModal()" style="display: none;" data-i18n="batchQueueDetailModal.addTask">添加任务</button>
|
||||||
<button class="btn-primary btn-small" id="batch-queue-start-btn" onclick="startBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.startExecute">开始执行</button>
|
<button class="btn-primary btn-small" id="batch-queue-start-btn" onclick="startBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.startExecute">开始执行</button>
|
||||||
|
<button class="btn-primary btn-small" id="batch-queue-rerun-btn" onclick="rerunBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.rerunQueue">重跑一轮</button>
|
||||||
<button class="btn-secondary btn-small" id="batch-queue-pause-btn" onclick="pauseBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.pauseQueue">暂停队列</button>
|
<button class="btn-secondary btn-small" id="batch-queue-pause-btn" onclick="pauseBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.pauseQueue">暂停队列</button>
|
||||||
<button class="btn-secondary btn-small btn-danger" id="batch-queue-delete-btn" onclick="deleteBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.deleteQueue">删除队列</button>
|
<button class="btn-secondary btn-small btn-danger" id="batch-queue-delete-btn" onclick="deleteBatchQueue()" style="display: none;" data-i18n="batchQueueDetailModal.deleteQueue">删除队列</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -2407,26 +2408,6 @@ version: 1.0.0<br>
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 编辑批量任务模态框 -->
|
|
||||||
<div id="edit-batch-task-modal" class="modal">
|
|
||||||
<div class="modal-content" style="max-width: 600px;">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h2 data-i18n="editBatchTaskModal.title">编辑任务</h2>
|
|
||||||
<span class="modal-close" onclick="closeEditBatchTaskModal()">×</span>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="edit-task-message" data-i18n="editBatchTaskModal.taskMessage">任务消息</label>
|
|
||||||
<textarea id="edit-task-message" class="form-control" rows="5" data-i18n="editBatchTaskModal.taskMessagePlaceholder" data-i18n-attr="placeholder" placeholder="请输入任务消息"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="form-actions">
|
|
||||||
<button class="btn-primary" onclick="saveBatchTask()" data-i18n="common.save">保存</button>
|
|
||||||
<button class="btn-secondary" onclick="closeEditBatchTaskModal()" data-i18n="common.cancel">取消</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 添加批量任务模态框 -->
|
<!-- 添加批量任务模态框 -->
|
||||||
<div id="add-batch-task-modal" class="modal">
|
<div id="add-batch-task-modal" class="modal">
|
||||||
<div class="modal-content" style="max-width: 600px;">
|
<div class="modal-content" style="max-width: 600px;">
|
||||||
|
|||||||
Reference in New Issue
Block a user