优化: 所有剩余UI/UX待办一批完成(持久化+AuditLog+解耦+total+原12大改+P2)
持久化(P1-c):新建 usePersistedRef composable,Tasks/AuditLog/ProjectDetail 等接入 localStorage
AuditLog(P1-d):后端 list_tool_executions 加 WHERE 筛选+返 {items,total,has_more},前端对接+长列折叠+筛选持久化
数据源解耦(P1-g):ProjectDetail projectTasks 按 project_id 独立加载 + ChatInput @项目联想独立加载(不读 store.tasks 当前页)
GitChanges(12a):后端 get_module_commits 加 git rev-list --count 返 total,前端显真实总数
原12大改:Dashboard 统计卡压底行(1)/Projects 列表卡片视图(2)/project_event 埋点排序(3)/TaskDetail 重设计(4)/IdeaDetail 重设计(5)/KnowledgeDetail 重设计(6)/界面持久化+侧栏Ctrl+B+审批数字键(7)/ProjectDetail 三栏改两栏(10)
P2打磨:文件浏览器(FileTree去重/FilePreview行号.md Diff/selectedFilePath归位)/settings反馈(假保存/端口校验)/AI会话(try-catch/scrollIntoView)/后端计数(move_queue事件/timeline total/workflow分页/import_batch分块)/杂项(TopBar/ConfirmDialog键盘/CIStatus i18n/ToolResultBody/ModuleNode/ApprovalDialog全选)
This commit is contained in:
@@ -72,7 +72,7 @@
|
||||
>
|
||||
<span v-if="processingId === tc.id" class="popup-btn-spinner"></span>
|
||||
<svg v-else width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
{{ $t('aiChat.dirAuthOnce') }}
|
||||
{{ $t('aiChat.dirAuthOnce') }}<span class="popup-btn-key">1</span>
|
||||
</button>
|
||||
<button
|
||||
class="popup-btn popup-btn--session"
|
||||
@@ -88,7 +88,7 @@
|
||||
@click.stop="onApproveAlways(tc)"
|
||||
>
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
{{ $t('aiChat.dirAuthAlways') }}
|
||||
{{ $t('aiChat.dirAuthAlways') }}<span class="popup-btn-key">2</span>
|
||||
</button>
|
||||
<button
|
||||
class="popup-btn popup-btn--reject"
|
||||
@@ -96,7 +96,7 @@
|
||||
@click.stop="onReject(tc)"
|
||||
>
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
{{ $t('aiChat.dirAuthDeny') }}
|
||||
{{ $t('aiChat.dirAuthDeny') }}<span class="popup-btn-key">3</span>
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="popup-item-actions">
|
||||
@@ -107,7 +107,7 @@
|
||||
>
|
||||
<span v-if="processingId === tc.id" class="popup-btn-spinner"></span>
|
||||
<svg v-else width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
|
||||
{{ $t('aiTool.approve') }}
|
||||
{{ $t('aiTool.approve') }}<span class="popup-btn-key">1</span>
|
||||
</button>
|
||||
<button
|
||||
class="popup-btn popup-btn--reject"
|
||||
@@ -115,7 +115,7 @@
|
||||
@click.stop="onReject(tc)"
|
||||
>
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
{{ $t('aiTool.reject') }}
|
||||
{{ $t('aiTool.reject') }}<span class="popup-btn-key">3</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -244,11 +244,45 @@ let _closeFallbackTimer: ReturnType<typeof setTimeout> | null = null
|
||||
const CLOSE_TIMEOUT_MS = 1500
|
||||
|
||||
/** Esc 键关浮窗(键盘可达性)。模块级命名函数,onBeforeUnmount 据此精确移除同一引用。
|
||||
* 仅响应 Esc(KeyDown),其他键透传(不拦截 Tab/Enter 等原生 button 导航)。 */
|
||||
* 仅响应 Esc(KeyDown),其他键透传(不拦截 Tab/Enter 等原生 button 导航)。
|
||||
*
|
||||
* 7c:数字键快捷审批(对列表顶部第一条生效,多审批场景用户应逐条点或用方向键导航)。
|
||||
* - path 类:1=once / 2=always / 3=deny(deny 仍走二次确认,与按钮同款不可逆守卫)
|
||||
* - risk 类:1=approve / 3=reject(2 仅 path 类有 always,risk 类忽略 '2')
|
||||
* 守卫:
|
||||
* - 确认对话框打开时(confirmState.visible)禁用快捷键,避免与确认按钮的 Enter/Esc 冲突
|
||||
* - 处理中(processingId)时忽略,防重复触发
|
||||
* - 输入元素聚焦时不拦截(input/textarea/contenteditable 场景,本浮窗暂无 input 但防御)
|
||||
* - Ctrl/Meta/Alt 组合不拦截(避免劫持浏览器/系统快捷键) */
|
||||
function _onKeyDown(e: KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault()
|
||||
void onClose()
|
||||
return
|
||||
}
|
||||
// 7c:数字键快捷审批
|
||||
if (e.key === '1' || e.key === '2' || e.key === '3') {
|
||||
// 组合键(Ctrl+1 等)不拦截;确认对话框打开时不拦截;处理中不拦截
|
||||
if (e.ctrlKey || e.metaKey || e.altKey) return
|
||||
if (confirmState.visible) return
|
||||
if (processingId.value) return
|
||||
// 输入元素聚焦时不拦截(防御:本浮窗无 input,但 ConfirmDialog 未来或有)
|
||||
const tgt = e.target as HTMLElement | null
|
||||
if (tgt && (tgt.tagName === 'INPUT' || tgt.tagName === 'TEXTAREA' || tgt.isContentEditable)) return
|
||||
const first = approvals.value[0]
|
||||
if (!first) return
|
||||
e.preventDefault()
|
||||
if (e.key === '1') {
|
||||
// 1 = once(path) / approve(risk)
|
||||
if (first.kind === 'path') void onApproveOnce(first)
|
||||
else void onApprove(first)
|
||||
} else if (e.key === '2') {
|
||||
// 2 = always(仅 path 类;risk 类无 always,忽略)
|
||||
if (first.kind === 'path') void onApproveAlways(first)
|
||||
} else {
|
||||
// 3 = deny(path) / reject(risk)
|
||||
void onReject(first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -683,6 +717,24 @@ function onDragEnd() { isDragging.value = false }
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
/* 7c:数字键快捷键标记(1/2/3 对应 once/approve、always、deny/reject)。
|
||||
小尺寸单字符徽章,提示键盘可达性;继承父按钮对比色但降透明度作辅助信息层。 */
|
||||
.popup-btn-key {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 13px;
|
||||
height: 13px;
|
||||
padding: 0 3px;
|
||||
margin-left: 1px;
|
||||
border-radius: var(--df-radius-xs, 3px);
|
||||
font-family: var(--df-font-mono);
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
opacity: 0.7;
|
||||
}
|
||||
.popup-btn--approve {
|
||||
background: var(--df-accent);
|
||||
color: #fff;
|
||||
|
||||
Reference in New Issue
Block a user