mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-06-29 09:55:56 +08:00
fix: 全面审查修复 14 个 bug,新增 Rust 单元测试
CRITICAL: - PathTable/MergePreview 操作后不重新渲染(加 dataVersion 版本号机制) - moveUp/moveDown 后 selectedIndices 过时(更新到新位置) HIGH: - ImportDialog 显示 "true" 而非路径数量(改为 number 类型) - F1 快捷键无效果(添加 onHelp 回调) - useKeyboard 每次渲染重复注册事件(改用 ref 模式) - batch delete 撤销顺序错误(拆分为独立记录) - importPaths 存储数组引用而非副本 - StringList.all 暴露内部数组(改为返回副本) - expand_env_vars 静默吞 API 错误(加 log::warn) - join_path 写入前未修剪路径(加 trim 避免注册表污染) MEDIUM: - handleClean 总传 () => true 不验证无效路径 - HelpDialog/ImportDialog 缺 Escape 关闭 - initDarkMode 不同步 Zustand store - 多处硬编码中文改为 i18n.t() - Rust unsafe 块补全 SAFETY 注释 新增 Rust 测试: system.rs 4 个单元测试 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+19
-12
@@ -1,4 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
|
||||
interface KeyboardActions {
|
||||
@@ -7,18 +7,21 @@ interface KeyboardActions {
|
||||
onDelete: () => void;
|
||||
onUndo: () => void;
|
||||
onRedo: () => void;
|
||||
onHelp: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局键盘快捷键
|
||||
* Ctrl+N 新建, Ctrl+S 保存, Ctrl+Z 撤销, Ctrl+Y 重做, Delete 删除
|
||||
* Ctrl+N 新建, Ctrl+S 保存, Ctrl+Z 撤销, Ctrl+Y 重做, Delete 删除, F1 帮助
|
||||
* 使用 ref 避免因 actions 对象每次渲染都是新引用而重复注册事件
|
||||
*/
|
||||
export function useKeyboard(actions: KeyboardActions) {
|
||||
const isAdmin = useAppStore((s) => s.isAdmin);
|
||||
const actionsRef = useRef(actions);
|
||||
actionsRef.current = actions;
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: KeyboardEvent) => {
|
||||
// 如果焦点在输入框中,只响应 Escape
|
||||
const tag = (e.target as HTMLElement)?.tagName;
|
||||
const isInput = tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT';
|
||||
|
||||
@@ -29,32 +32,36 @@ export function useKeyboard(actions: KeyboardActions) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isAdmin) return;
|
||||
|
||||
const a = actionsRef.current;
|
||||
const ctrl = e.ctrlKey || e.metaKey;
|
||||
|
||||
if (ctrl && e.key === 'z') {
|
||||
if (!isAdmin) return;
|
||||
e.preventDefault();
|
||||
actions.onUndo();
|
||||
a.onUndo();
|
||||
} else if (ctrl && e.key === 'y') {
|
||||
if (!isAdmin) return;
|
||||
e.preventDefault();
|
||||
actions.onRedo();
|
||||
a.onRedo();
|
||||
} else if (ctrl && e.key === 'n') {
|
||||
if (!isAdmin) return;
|
||||
e.preventDefault();
|
||||
actions.onNew();
|
||||
a.onNew();
|
||||
} else if (ctrl && e.key === 's') {
|
||||
if (!isAdmin) return;
|
||||
e.preventDefault();
|
||||
actions.onSave();
|
||||
a.onSave();
|
||||
} else if (e.key === 'Delete' || e.key === 'Backspace') {
|
||||
if (!isAdmin) return;
|
||||
e.preventDefault();
|
||||
actions.onDelete();
|
||||
a.onDelete();
|
||||
} else if (e.key === 'F1') {
|
||||
e.preventDefault();
|
||||
// 帮助由 AppShell 处理
|
||||
a.onHelp();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handler);
|
||||
return () => window.removeEventListener('keydown', handler);
|
||||
}, [isAdmin, actions]);
|
||||
}, [isAdmin]); // 只依赖 isAdmin,actions 通过 ref 读取
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user