Add files via upload

This commit is contained in:
公明
2026-07-07 17:17:19 +08:00
committed by GitHub
parent 8ef47474ff
commit dd4c22a1bd
3 changed files with 477 additions and 47 deletions
+237 -15
View File
@@ -15842,17 +15842,17 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
.batch-queue-detail-kv {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 10px 20px;
margin-bottom: 14px;
grid-template-columns: repeat(2, minmax(280px, 1fr));
gap: 12px 32px;
margin-bottom: 18px;
}
.bq-kv {
display: grid;
grid-template-columns: minmax(100px, 34%) 1fr;
gap: 8px 12px;
align-items: start;
padding: 8px 0;
grid-template-columns: minmax(88px, 112px) 1fr;
gap: 10px 16px;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid var(--border-color);
font-size: 0.875rem;
}
@@ -15917,22 +15917,34 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
/* Inline edit controls (replaces value in-place) */
.bq-inline-edit-controls {
display: inline-flex;
display: flex;
align-items: center;
gap: 6px;
gap: 10px;
flex-wrap: wrap;
width: 100%;
min-width: 0;
}
.bq-inline-edit-controls input[type="text"],
.bq-inline-edit-controls select {
padding: 4px 8px;
border-radius: 6px;
padding: 8px 12px;
border-radius: 8px;
border: 1.5px solid var(--accent-color);
font-size: 13px;
font-size: 0.875rem;
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"] {
flex: 1;
min-width: 200px;
}
.bq-inline-edit-controls .bq-edit-cron-expr {
flex: 1 1 280px;
min-width: 240px;
max-width: 100%;
}
.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);
@@ -15960,11 +15972,18 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible {
.batch-task-inline-edit textarea:focus {
box-shadow: 0 0 0 2px rgba(0, 102, 255, 0.15);
}
@media (max-width: 720px) {
.batch-queue-detail-kv {
grid-template-columns: 1fr;
gap: 8px;
}
}
@media (max-width: 520px) {
.bq-kv,
.bq-kv--block {
grid-template-columns: 1fr;
gap: 4px;
gap: 6px;
}
}
@@ -29318,7 +29337,8 @@ html[data-theme="dark"] .workflow-form-select-option:hover,
html[data-theme="dark"] .projects-filter-select-option:hover,
html[data-theme="dark"] .chat-files-filter-select-option:hover,
html[data-theme="dark"] #role-modal .role-form-select-option:hover,
html[data-theme="dark"] .monitor-filter-select-option:hover {
html[data-theme="dark"] .monitor-filter-select-option:hover,
html[data-theme="dark"] .batch-form-select-option:hover {
background: rgba(96, 165, 250, 0.12);
}
@@ -29334,7 +29354,8 @@ html[data-theme="dark"] .workflow-form-select-option.is-selected,
html[data-theme="dark"] .projects-filter-select-option.is-selected,
html[data-theme="dark"] .chat-files-filter-select-option.is-selected,
html[data-theme="dark"] #role-modal .role-form-select-option.is-selected,
html[data-theme="dark"] .monitor-filter-select-option.is-selected {
html[data-theme="dark"] .monitor-filter-select-option.is-selected,
html[data-theme="dark"] .batch-form-select-option.is-selected {
background: rgba(96, 165, 250, 0.18);
color: var(--accent-hover);
}
@@ -29355,6 +29376,7 @@ html[data-theme="dark"] .workflow-tool-select-trigger,
html[data-theme="dark"] .projects-filter-select-trigger,
html[data-theme="dark"] .chat-files-filter-select-trigger,
html[data-theme="dark"] .role-form-select-trigger,
html[data-theme="dark"] .batch-form-select-trigger,
html[data-theme="dark"] .monitor-filter-select-trigger,
html[data-theme="dark"] .role-selector-btn,
html[data-theme="dark"] .conversation-project-filter-trigger {
@@ -29383,6 +29405,7 @@ html[data-theme="dark"] .workflow-form-select-ui.open .workflow-form-select-trig
html[data-theme="dark"] .projects-filter-select-ui.open .projects-filter-select-trigger,
html[data-theme="dark"] .chat-files-filter-select-ui.open .chat-files-filter-select-trigger,
html[data-theme="dark"] #role-modal .role-form-select-ui.open .role-form-select-trigger,
html[data-theme="dark"] .batch-form-select-ui.open .batch-form-select-trigger,
html[data-theme="dark"] .monitor-filter-select-ui.open .monitor-filter-select-trigger {
border-color: var(--accent-color);
box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.16);
@@ -31119,6 +31142,205 @@ html[data-theme="dark"] #agent-md-modal .modal-footer,
white-space: nowrap;
}
/* 批量任务:新建队列 / 详情内联编辑 — 自定义下拉 */
.batch-form-native-select {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
#batch-import-modal .batch-form-select-ui,
#batch-queue-detail-modal .batch-form-select-ui {
position: relative;
width: 100%;
min-width: 0;
}
.batch-form-select-ui--inline {
display: block;
width: 100%;
min-width: 220px;
max-width: none;
}
.batch-form-select-trigger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
width: 100%;
min-width: 0;
min-height: 40px;
padding: 10px 12px;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--bg-primary);
color: var(--text-primary);
font-size: 0.875rem;
line-height: 1.25;
cursor: pointer;
font-family: inherit;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.batch-form-select-ui--inline .batch-form-select-trigger {
min-height: 36px;
padding: 8px 12px;
font-size: 0.875rem;
border-radius: 8px;
border-width: 1.5px;
border-color: var(--accent-color);
}
.batch-form-select-trigger:hover:not(:disabled) {
border-color: rgba(59, 130, 246, 0.45);
}
.batch-form-select-ui.open .batch-form-select-trigger {
border-color: var(--accent-color);
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.12);
}
.batch-form-select-ui.open {
z-index: 120;
}
.batch-form-select-ui.is-disabled .batch-form-select-trigger {
opacity: 0.6;
cursor: not-allowed;
}
.batch-form-select-value {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: left;
}
.batch-form-select-caret {
flex-shrink: 0;
color: var(--text-secondary);
transition: transform 0.15s ease;
}
.batch-form-select-ui.open .batch-form-select-caret {
transform: rotate(180deg);
}
.batch-form-select-dropdown {
display: none;
position: absolute;
top: calc(100% + 4px);
left: 0;
right: auto;
min-width: 100%;
width: max-content;
max-width: min(420px, calc(100vw - 48px));
z-index: 200;
max-height: 280px;
overflow-y: auto;
padding: 4px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 8px;
box-shadow: var(--shadow-lg);
}
.batch-form-select-ui.open .batch-form-select-dropdown {
display: block;
}
.batch-form-select-option {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
padding: 8px 10px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--text-primary);
font-size: 0.8125rem;
font-family: inherit;
cursor: pointer;
text-align: left;
transition: background 0.12s ease, color 0.12s ease;
}
.batch-form-select-option:hover {
background: var(--bg-secondary);
}
.batch-form-select-option.is-selected {
background: rgba(59, 130, 246, 0.08);
color: #2563eb;
font-weight: 500;
}
.batch-form-select-check {
width: 14px;
flex-shrink: 0;
visibility: hidden;
color: #2563eb;
font-size: 12px;
line-height: 1;
}
.batch-form-select-option.is-selected .batch-form-select-check {
visibility: visible;
}
.batch-form-select-label {
flex: 1;
min-width: 0;
white-space: nowrap;
}
.batch-form-select-ui--inline .batch-form-select-value {
white-space: nowrap;
overflow: visible;
text-overflow: unset;
}
.batch-form-select-option .batch-form-select-label {
overflow: visible;
text-overflow: unset;
white-space: nowrap;
}
#batch-import-modal .modal-content:has(.batch-form-select-ui.open),
#batch-import-modal .modal-body:has(.batch-form-select-ui.open),
#batch-queue-detail-modal .modal-content:has(.batch-form-select-ui.open),
#batch-queue-detail-modal .modal-body:has(.batch-form-select-ui.open) {
overflow: visible;
}
.bq-inline-edit-controls .batch-form-native-select {
position: absolute;
}
.bq-inline-edit-controls .batch-form-select-ui--inline {
margin: 0;
flex: 1 1 240px;
}
#batch-queue-detail-modal .bq-kv__v:has(.bq-inline-edit-controls) {
overflow: visible;
}
#batch-queue-detail-modal .batch-queue-detail-tech__sum {
white-space: normal;
line-height: 1.5;
}
html[data-theme="dark"] #role-modal .modal-content,
html[data-theme="dark"] #role-modal .modal-header,
html[data-theme="dark"] #role-modal .modal-body,
+236 -28
View File
@@ -765,6 +765,7 @@ function toggleTasksAutoRefresh(enabled) {
// 初始化任务管理页面
function initTasksPage() {
initBatchQueuesFilterSelects();
initBatchFormSelects();
// 恢复自动刷新设置
const autoRefreshCheckbox = document.getElementById('tasks-auto-refresh');
if (autoRefreshCheckbox) {
@@ -930,6 +931,7 @@ async function showBatchImportModal() {
}
}
await refreshBatchProjectSelectOptions();
refreshBatchFormSelects();
openAppModal('batch-import-modal', { focusEl: input });
}
@@ -1299,6 +1301,207 @@ function initBatchQueuesFilterSelects() {
syncAllBatchQueuesFilterSelects();
}
const BATCH_IMPORT_FORM_SELECT_IDS = [
'batch-queue-role',
'batch-queue-project-id',
'batch-queue-agent-mode',
'batch-queue-schedule-mode',
];
const batchFormSelectMap = {};
let batchFormSelectDocBound = false;
const BATCH_FORM_SELECT_CARET = '<svg class="batch-form-select-caret" width="14" height="14" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
function closeAllBatchFormSelects() {
Object.keys(batchFormSelectMap).forEach(function (id) {
const reg = batchFormSelectMap[id];
if (!reg || !reg.wrapper) return;
reg.wrapper.classList.remove('open');
if (reg.trigger) reg.trigger.setAttribute('aria-expanded', 'false');
});
}
function syncBatchFormSelect(selectId) {
const reg = batchFormSelectMap[selectId];
if (!reg) return;
const select = reg.select;
const dropdown = reg.dropdown;
const trigger = reg.trigger;
const valueSpan = trigger.querySelector('.batch-form-select-value');
dropdown.innerHTML = '';
Array.prototype.forEach.call(select.options, function (opt) {
const item = document.createElement('button');
item.type = 'button';
item.className = 'batch-form-select-option';
item.setAttribute('role', 'option');
item.setAttribute('data-value', opt.value);
if (opt.value === select.value) {
item.classList.add('is-selected');
item.setAttribute('aria-selected', 'true');
} else {
item.setAttribute('aria-selected', 'false');
}
const check = document.createElement('span');
check.className = 'batch-form-select-check';
check.setAttribute('aria-hidden', 'true');
check.textContent = '✓';
const label = document.createElement('span');
label.className = 'batch-form-select-label';
label.textContent = opt.textContent;
item.appendChild(check);
item.appendChild(label);
dropdown.appendChild(item);
});
const selectedOpt = select.options[select.selectedIndex];
if (valueSpan) {
valueSpan.textContent = selectedOpt ? selectedOpt.textContent : '';
}
trigger.disabled = !!select.disabled;
reg.wrapper.classList.toggle('is-disabled', !!select.disabled);
}
function syncAllBatchImportFormSelects() {
BATCH_IMPORT_FORM_SELECT_IDS.forEach(syncBatchFormSelect);
}
function isBatchFormSelectFocused(selectId) {
const active = document.activeElement;
if (!active) return false;
if (active.id === selectId) return true;
const reg = batchFormSelectMap[selectId];
return !!(reg && reg.wrapper && reg.wrapper.contains(active));
}
function focusBatchFormSelect(selectId) {
const reg = batchFormSelectMap[selectId];
if (reg && reg.trigger) {
reg.trigger.focus();
return;
}
const select = document.getElementById(selectId);
if (select) select.focus();
}
function bindBatchInlineEditFocusOut(container, saveFn, isCancelledFn) {
if (!container) return;
container.addEventListener('focusout', function () {
setTimeout(function () {
if (isCancelledFn && isCancelledFn()) return;
if (container.contains(document.activeElement)) return;
saveFn();
}, 100);
});
}
function enhanceBatchFormSelect(selectId, options) {
options = options || {};
const select = document.getElementById(selectId);
if (!select) return;
const existing = batchFormSelectMap[selectId];
if (existing && existing.select !== select) {
delete batchFormSelectMap[selectId];
}
if (select.dataset.batchFormCustom === '1') {
syncBatchFormSelect(selectId);
return;
}
select.dataset.batchFormCustom = '1';
select.classList.add('batch-form-native-select');
select.tabIndex = -1;
select.setAttribute('aria-hidden', 'true');
const wrapper = document.createElement('div');
wrapper.className = 'batch-form-select-ui' + (options.inline ? ' batch-form-select-ui--inline' : '');
const trigger = document.createElement('button');
trigger.type = 'button';
trigger.className = 'batch-form-select-trigger';
trigger.setAttribute('aria-haspopup', 'listbox');
trigger.setAttribute('aria-expanded', 'false');
const valueSpan = document.createElement('span');
valueSpan.className = 'batch-form-select-value';
trigger.appendChild(valueSpan);
trigger.insertAdjacentHTML('beforeend', BATCH_FORM_SELECT_CARET);
const dropdown = document.createElement('div');
dropdown.className = 'batch-form-select-dropdown';
dropdown.setAttribute('role', 'listbox');
const parent = select.parentNode;
parent.insertBefore(wrapper, select);
wrapper.appendChild(trigger);
wrapper.appendChild(dropdown);
wrapper.appendChild(select);
batchFormSelectMap[selectId] = { wrapper: wrapper, trigger: trigger, dropdown: dropdown, select: select };
trigger.addEventListener('click', function (e) {
e.stopPropagation();
if (select.disabled) return;
const open = wrapper.classList.contains('open');
closeAllBatchFormSelects();
if (!open) {
wrapper.classList.add('open');
trigger.setAttribute('aria-expanded', 'true');
}
});
dropdown.addEventListener('click', function (e) {
const opt = e.target.closest('.batch-form-select-option');
if (!opt) return;
e.stopPropagation();
const val = opt.getAttribute('data-value');
if (val === null) return;
if (select.value !== val) {
select.value = val;
select.dispatchEvent(new Event('change', { bubbles: true }));
}
wrapper.classList.remove('open');
trigger.setAttribute('aria-expanded', 'false');
syncBatchFormSelect(selectId);
});
select.addEventListener('change', function () {
syncBatchFormSelect(selectId);
});
syncBatchFormSelect(selectId);
}
function refreshBatchFormSelect(selectId, options) {
const select = document.getElementById(selectId);
if (!select) {
delete batchFormSelectMap[selectId];
return;
}
enhanceBatchFormSelect(selectId, options);
}
function refreshBatchFormSelects() {
Object.keys(batchFormSelectMap).forEach(function (id) {
if (!document.getElementById(id)) delete batchFormSelectMap[id];
});
BATCH_IMPORT_FORM_SELECT_IDS.forEach(function (id) {
enhanceBatchFormSelect(id, { inline: false });
});
if (!batchFormSelectDocBound) {
batchFormSelectDocBound = true;
document.addEventListener('click', function (e) {
if (e.target.closest('.batch-form-select-ui')) return;
closeAllBatchFormSelects();
});
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') closeAllBatchFormSelects();
});
}
syncAllBatchImportFormSelects();
}
function initBatchFormSelects() {
refreshBatchFormSelects();
}
// 筛选批量任务队列
function filterBatchQueues() {
const statusFilter = document.getElementById('batch-queues-status-filter');
@@ -2337,15 +2540,20 @@ function startInlineEditRole() {
${orphanOpt}${opts}
</select>
</span>`;
refreshBatchFormSelect('bq-edit-role', { inline: true });
const sel = document.getElementById('bq-edit-role');
const controls = container.querySelector('.bq-inline-edit-controls');
const roleReg = batchFormSelectMap['bq-edit-role'];
if (sel) {
sel.focus();
focusBatchFormSelect('bq-edit-role');
let cancelled = false;
sel.addEventListener('keydown', (e) => {
const onEscape = (e) => {
if (e.key === 'Escape') { cancelled = true; cancelAllInlineEdits(); }
});
};
sel.addEventListener('keydown', onEscape);
if (roleReg && roleReg.trigger) roleReg.trigger.addEventListener('keydown', onEscape);
sel.addEventListener('change', () => { if (!cancelled) saveInlineRole(); });
sel.addEventListener('blur', () => { if (!cancelled) saveInlineRole(); });
bindBatchInlineEditFocusOut(controls, () => { if (!cancelled) saveInlineRole(); }, () => cancelled);
}
});
}
@@ -2397,15 +2605,20 @@ function startInlineEditAgentMode() {
<option value="supervisor" ${currentMode === 'supervisor' ? 'selected' : ''}>${escapeHtml(_t('chat.agentModeSupervisorLabel'))}</option>
</select>
</span>`;
refreshBatchFormSelect('bq-edit-agentmode', { inline: true });
const sel = document.getElementById('bq-edit-agentmode');
const controls = container.querySelector('.bq-inline-edit-controls');
const modeReg = batchFormSelectMap['bq-edit-agentmode'];
if (sel) {
sel.focus();
focusBatchFormSelect('bq-edit-agentmode');
let cancelled = false;
sel.addEventListener('keydown', (e) => {
const onEscape = (e) => {
if (e.key === 'Escape') { cancelled = true; cancelAllInlineEdits(); }
});
};
sel.addEventListener('keydown', onEscape);
if (modeReg && modeReg.trigger) modeReg.trigger.addEventListener('keydown', onEscape);
sel.addEventListener('change', () => { if (!cancelled) saveInlineAgentMode(); });
sel.addEventListener('blur', () => { if (!cancelled) saveInlineAgentMode(); });
bindBatchInlineEditFocusOut(controls, () => { if (!cancelled) saveInlineAgentMode(); }, () => cancelled);
}
});
}
@@ -2543,45 +2756,38 @@ function startInlineEditSchedule() {
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()">
<select id="bq-edit-schedule-mode">
<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;' : ''}" />
<input type="text" id="bq-edit-cron-expr" class="bq-edit-cron-expr" value="${escapeHtml(queue.cronExpr || '')}" placeholder="${_t('batchImportModal.cronExprPlaceholder', { interpolation: { escapeValue: false } })}" style="${!isCron ? 'display:none;' : ''}" />
</span>`;
refreshBatchFormSelect('bq-edit-schedule-mode', { inline: true });
let schedCancelled = false;
const sel = document.getElementById('bq-edit-schedule-mode');
const cronInp = document.getElementById('bq-edit-cron-expr');
const controls = container.querySelector('.bq-inline-edit-controls');
const schedReg = batchFormSelectMap['bq-edit-schedule-mode'];
const onSchedEscape = (e) => { if (e.key === 'Escape') { schedCancelled = true; cancelAllInlineEdits(); } };
if (sel) {
sel.focus();
sel.addEventListener('keydown', (e) => { if (e.key === 'Escape') { schedCancelled = true; cancelAllInlineEdits(); } });
focusBatchFormSelect('bq-edit-schedule-mode');
sel.addEventListener('keydown', onSchedEscape);
if (schedReg && schedReg.trigger) schedReg.trigger.addEventListener('keydown', onSchedEscape);
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);
});
}
bindBatchInlineEditFocusOut(controls, () => {
if (schedCancelled) return;
saveInlineSchedule();
}, () => schedCancelled);
});
}
function toggleInlineScheduleCron() {
@@ -2675,6 +2881,7 @@ window.saveInlineSchedule = saveInlineSchedule;
document.addEventListener('languagechange', function () {
try {
syncAllBatchQueuesFilterSelects();
syncAllBatchImportFormSelects();
const tasksPage = document.getElementById('page-tasks');
if (!tasksPage || !tasksPage.classList.contains('active')) {
return;
@@ -2697,4 +2904,5 @@ document.addEventListener('languagechange', function () {
document.addEventListener('DOMContentLoaded', function () {
initBatchQueuesFilterSelects();
initBatchFormSelects();
});