feat(app): 添加图片采集器功能并修复粘贴问题

- 集成 dexie 和 dexie-react-hooks 实现本地数据库存储
- 创建 TestPage 组件实现图片粘贴采集功能
- 修复重复粘贴问题,添加事件监听器清理机制
- 修复删除按钮触发增加的事件冒泡问题
- 为电子木鱼页面添加顶部边距样式
- 更新导航项标签为中文显示
- 添加图片网格展示和删除功能
This commit is contained in:
雨霖铃
2026-01-08 21:49:39 +08:00
parent 3ee28a91e0
commit 7079334562
6 changed files with 143 additions and 1 deletions
+19
View File
@@ -12,6 +12,8 @@
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1", "@testing-library/react": "^16.3.1",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"dexie": "^4.2.1",
"dexie-react-hooks": "^4.2.0",
"react": "^19.2.3", "react": "^19.2.3",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
@@ -6703,6 +6705,23 @@
"url": "https://github.com/sponsors/wooorm" "url": "https://github.com/sponsors/wooorm"
} }
}, },
"node_modules/dexie": {
"version": "4.2.1",
"resolved": "https://registry.npmmirror.com/dexie/-/dexie-4.2.1.tgz",
"integrity": "sha512-Ckej0NS6jxQ4Po3OrSQBFddayRhTCic2DoCAG5zacOfOVB9P2Q5Xc5uL/nVa7ZVs+HdMnvUPzLFCB/JwpB6Csg==",
"license": "Apache-2.0"
},
"node_modules/dexie-react-hooks": {
"version": "4.2.0",
"resolved": "https://registry.npmmirror.com/dexie-react-hooks/-/dexie-react-hooks-4.2.0.tgz",
"integrity": "sha512-u7KqTX9JpBQK8+tEyA9X0yMGXlSCsbm5AU64N6gjvGk/IutYDpLBInMYEAEC83s3qhIvryFS+W+sqLZUBEvePQ==",
"license": "Apache-2.0",
"peerDependencies": {
"@types/react": ">=16",
"dexie": ">=4.2.0-alpha.1 <5.0.0",
"react": ">=16"
}
},
"node_modules/didyoumean": { "node_modules/didyoumean": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz",
+2
View File
@@ -7,6 +7,8 @@
"@testing-library/jest-dom": "^6.9.1", "@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^16.3.1", "@testing-library/react": "^16.3.1",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"dexie": "^4.2.1",
"dexie-react-hooks": "^4.2.0",
"react": "^19.2.3", "react": "^19.2.3",
"react-dom": "^19.2.3", "react-dom": "^19.2.3",
"react-markdown": "^10.1.0", "react-markdown": "^10.1.0",
+3 -1
View File
@@ -4,13 +4,15 @@ import TimestampPage from './pages/TimestampPage';
import ElectronicWoodenFishPage from "./pages/ElectronicWoodenFishPage"; import ElectronicWoodenFishPage from "./pages/ElectronicWoodenFishPage";
import TodoListPage from "./pages/TodoListPage"; import TodoListPage from "./pages/TodoListPage";
import './App.css'; import './App.css';
import TestPage from "./pages/TestPage";
// 导航项配置数组,便于后续添加 // 导航项配置数组,便于后续添加
// 为了测试折叠功能,我们添加更多导航项 // 为了测试折叠功能,我们添加更多导航项
const navItems = [ const navItems = [
{path: '/', label: 'Timestamp', element: <TimestampPage/>}, {path: '/', label: '时间戳', element: <TimestampPage/>},
{path: '/dzmy', label: '电子木鱼', element: <ElectronicWoodenFishPage/>}, {path: '/dzmy', label: '电子木鱼', element: <ElectronicWoodenFishPage/>},
{path: '/todo', label: '待办', element: <TodoListPage/>}, {path: '/todo', label: '待办', element: <TodoListPage/>},
{path: '/test', label: '测试', element: <TestPage/>}
]; ];
function App() { function App() {
+1
View File
@@ -30,6 +30,7 @@
border: 1px solid rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.05);
overflow: hidden; overflow: hidden;
padding: 20px; padding: 20px;
margin-top: 20px;
} }
/* 添加装饰性背景元素 */ /* 添加装饰性背景元素 */
+111
View File
@@ -0,0 +1,111 @@
import React, {useEffect} from 'react';
import {db} from '../utils/db';
import {useLiveQuery} from 'dexie-react-hooks';
// 样式简单写一下,实际可以用 styled-components 或 css modules
const styles = {
container: {padding: '20px', fontFamily: 'sans-serif', maxWidth: '600px', margin: '0 auto'},
uploadBox: {
border: '2px dashed #aaa',
padding: '40px',
textAlign: 'center',
background: '#f9f9f9',
marginBottom: '20px',
borderRadius: '8px'
},
grid: {display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(150px, 1fr))', gap: '10px'},
card: {border: '1px solid #eee', borderRadius: '8px', overflow: 'hidden', position: 'relative'},
img: {width: '100%', height: '120px', objectFit: 'cover', display: 'block'},
delBtn: {
position: 'absolute',
top: '5px',
right: '5px',
background: 'red',
color: 'white',
border: 'none',
cursor: 'pointer',
padding: '2px 8px',
borderRadius: '4px'
}
};
const TestPage = () => {
const images = useLiveQuery(() => db.images.orderBy('created').reverse().toArray());
// --- FIX 2: 修复重复粘贴问题 ---
useEffect(() => {
const handlePaste = async (event) => {
// 1. 获取剪贴板数据
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
// 2. 找到第一个图片文件 (防止一次粘贴循环出多个格式)
let foundImage = false;
for (let item of items) {
if (!foundImage && item.kind === 'file' && item.type.includes('image/')) {
const blob = item.getAsFile();
foundImage = true; // 标记已找到,避免重复处理
// 3. 阻止默认行为(防止浏览器把图片直接贴到页面上)
event.preventDefault();
await db.images.add({
blob: blob,
created: new Date(),
source: 'Paste'
});
console.log('图片已添加');
}
}
};
// 添加监听
window.addEventListener('paste', handlePaste);
// ★★★ 关键:必须返回一个清理函数,组件刷新时移除旧的监听器 ★★★
return () => {
window.removeEventListener('paste', handlePaste);
};
}, []); // ★★★ 关键:依赖数组必须为空 [],保证只在挂载时绑定一次
// --- FIX 1: 修复删除变增加的问题 ---
const handleDelete = async (event, id) => {
// ★★★ 关键:阻止事件冒泡,防止触发父级的点击或粘贴逻辑
event.stopPropagation();
try {
await db.images.delete(id);
console.log('删除成功 ID:', id);
} catch (error) {
console.error('删除失败:', error);
}
};
return (
<div style={styles.container}>
<h2>React 图片采集器 (已修复)</h2>
<p>点击任意处粘贴 (Ctrl+V)</p>
<div style={styles.grid}>
{images?.map((img) => (
<div key={img.id} style={styles.card}>
<img src={URL.createObjectURL(img.blob)} alt="screenshot" style={styles.img}/>
{/* 传递 event 对象给 handleDelete */}
<button
style={styles.delBtn}
onClick={(e) => handleDelete(e, img.id)}
>
删除
</button>
</div>
))}
</div>
</div>
);
}
export default TestPage;
+7
View File
@@ -0,0 +1,7 @@
import Dexie from "dexie";
export const db = new Dexie("testImagesDb");
db.version(1).stores({
images: '++id, created, source'
});