diff --git a/package-lock.json b/package-lock.json index 9fb9ec2..7a18029 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.1", "@testing-library/user-event": "^13.5.0", + "dexie": "^4.2.1", + "dexie-react-hooks": "^4.2.0", "react": "^19.2.3", "react-dom": "^19.2.3", "react-markdown": "^10.1.0", @@ -6703,6 +6705,23 @@ "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": { "version": "1.2.2", "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", diff --git a/package.json b/package.json index 80001b5..c2655d0 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.1", "@testing-library/user-event": "^13.5.0", + "dexie": "^4.2.1", + "dexie-react-hooks": "^4.2.0", "react": "^19.2.3", "react-dom": "^19.2.3", "react-markdown": "^10.1.0", diff --git a/src/App.js b/src/App.js index 13403a8..acce7b5 100644 --- a/src/App.js +++ b/src/App.js @@ -4,13 +4,15 @@ import TimestampPage from './pages/TimestampPage'; import ElectronicWoodenFishPage from "./pages/ElectronicWoodenFishPage"; import TodoListPage from "./pages/TodoListPage"; import './App.css'; +import TestPage from "./pages/TestPage"; // 导航项配置数组,便于后续添加 // 为了测试折叠功能,我们添加更多导航项 const navItems = [ - {path: '/', label: 'Timestamp', element: }, + {path: '/', label: '时间戳', element: }, {path: '/dzmy', label: '电子木鱼', element: }, {path: '/todo', label: '待办', element: }, + {path: '/test', label: '测试', element: } ]; function App() { diff --git a/src/pages/ElectronicWoodenFish.css b/src/pages/ElectronicWoodenFish.css index b4a3ff6..1653dc5 100644 --- a/src/pages/ElectronicWoodenFish.css +++ b/src/pages/ElectronicWoodenFish.css @@ -30,6 +30,7 @@ border: 1px solid rgba(255, 255, 255, 0.05); overflow: hidden; padding: 20px; + margin-top: 20px; } /* 添加装饰性背景元素 */ diff --git a/src/pages/TestPage.js b/src/pages/TestPage.js new file mode 100644 index 0000000..6e40272 --- /dev/null +++ b/src/pages/TestPage.js @@ -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 ( +
+

React 图片采集器 (已修复)

+

点击任意处粘贴 (Ctrl+V)

+ +
+ {images?.map((img) => ( +
+ screenshot + + {/* 传递 event 对象给 handleDelete */} + + +
+ ))} +
+
+ ); +} + +export default TestPage; \ No newline at end of file diff --git a/src/utils/db.js b/src/utils/db.js new file mode 100644 index 0000000..32166ce --- /dev/null +++ b/src/utils/db.js @@ -0,0 +1,7 @@ +import Dexie from "dexie"; + +export const db = new Dexie("testImagesDb"); + +db.version(1).stores({ + images: '++id, created, source' +}); \ No newline at end of file