feat(app): 添加图片采集器功能并修复粘贴问题
- 集成 dexie 和 dexie-react-hooks 实现本地数据库存储 - 创建 TestPage 组件实现图片粘贴采集功能 - 修复重复粘贴问题,添加事件监听器清理机制 - 修复删除按钮触发增加的事件冒泡问题 - 为电子木鱼页面添加顶部边距样式 - 更新导航项标签为中文显示 - 添加图片网格展示和删除功能
This commit is contained in:
Generated
+19
@@ -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",
|
||||||
|
|||||||
@@ -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
@@ -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() {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 添加装饰性背景元素 */
|
/* 添加装饰性背景元素 */
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import Dexie from "dexie";
|
||||||
|
|
||||||
|
export const db = new Dexie("testImagesDb");
|
||||||
|
|
||||||
|
db.version(1).stores({
|
||||||
|
images: '++id, created, source'
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user