1+ import PropTypes from 'prop-types' ;
2+ import React from 'react' ;
3+ import { connect } from 'react-redux' ;
4+ import bindAll from 'lodash.bindall' ;
5+
6+ import DebugWindowComponent from '../components/debug-window/debug-window.jsx' ;
7+
8+ const DEFAULT_VALUE = `[
9+ {
10+ "id": 3,
11+ "type": "script",
12+ "mime": "application/json",
13+ "name": "code",
14+ "bodyData": [91, 123, 34, 105, 100, 34]
15+ }
16+ ]` ;
17+
18+ class DebugWindow extends React . Component {
19+ constructor ( props ) {
20+ super ( props ) ;
21+ bindAll ( this , [
22+ 'handleCancel' ,
23+ 'handleChange' ,
24+ 'handleOk' ,
25+ 'handleKeyDown' ,
26+ 'handleKeyUp'
27+ ] ) ;
28+ this . state = {
29+ value : DEFAULT_VALUE ,
30+ error : null ,
31+ success : null
32+ } ;
33+ this . _zPressed = false ;
34+ }
35+
36+ componentDidMount ( ) {
37+ document . addEventListener ( 'keydown' , this . handleKeyDown ) ;
38+ document . addEventListener ( 'keyup' , this . handleKeyUp ) ;
39+ }
40+
41+ componentWillUnmount ( ) {
42+ document . removeEventListener ( 'keydown' , this . handleKeyDown ) ;
43+ document . removeEventListener ( 'keyup' , this . handleKeyUp ) ;
44+ }
45+
46+ handleKeyDown ( event ) {
47+ // 检测 Ctrl/Cmd + Shift + Z 三键同时按下
48+ if (
49+ ( event . ctrlKey || event . metaKey ) &&
50+ event . shiftKey &&
51+ event . key . toLowerCase ( ) === 'z' &&
52+ ! event . repeat
53+ ) {
54+ // 当 Ctrl/Cmd + Shift + Z 按下时,标记 Z 键已按下
55+ this . _zPressed = true ;
56+ this . _zEvent = event ;
57+ }
58+
59+ // 如果在 Z 按下后紧接着检测到 9 键(四键同时按下)
60+ // 使用 event.code 以兼容不同键盘布局(Shift+9 在不同布局下 event.key 不同)
61+ if (
62+ this . _zPressed &&
63+ ( event . ctrlKey || event . metaKey ) &&
64+ event . shiftKey &&
65+ event . code === 'Digit9' &&
66+ ! event . repeat
67+ ) {
68+ event . preventDefault ( ) ;
69+ event . stopPropagation ( ) ;
70+ this . _zPressed = false ;
71+ this . props . onToggleDebugWindow ( ) ;
72+ }
73+ }
74+
75+ handleKeyUp ( event ) {
76+ if ( event . key . toLowerCase ( ) === 'z' || event . code === 'KeyZ' ) {
77+ this . _zPressed = false ;
78+ }
79+ if ( event . code === 'Digit9' ) {
80+ this . _zPressed = false ;
81+ }
82+ }
83+
84+ handleCancel ( ) {
85+ this . props . onToggleDebugWindow ( ) ;
86+ }
87+
88+ handleChange ( event ) {
89+ this . setState ( {
90+ value : event . target . value ,
91+ error : null ,
92+ success : null
93+ } ) ;
94+ }
95+
96+ handleOk ( ) {
97+ this . setState ( { error : null , success : null } ) ;
98+
99+ try {
100+ // 解析用户输入的 JSON
101+ let myData ;
102+ try {
103+ myData = JSON . parse ( this . state . value ) ;
104+ } catch ( e ) {
105+ this . setState ( { error : `JSON 解析错误: ${ e . message } ` } ) ;
106+ return ;
107+ }
108+
109+ const dbName = 'TW_Backpack' ;
110+ const storeName = 'backpack' ;
111+
112+ // 如果是数组,自动取出第一个对象
113+ if ( Array . isArray ( myData ) ) {
114+ myData = myData [ 0 ] ;
115+ }
116+
117+ // 健壮性检查
118+ if ( ! myData ) {
119+ this . setState ( { error : '错误:传入的数据数组为空或无效!' } ) ;
120+ return ;
121+ }
122+
123+ const request = indexedDB . open ( dbName ) ;
124+
125+ request . onsuccess = ( event ) => {
126+ const db = event . target . result ;
127+
128+ const transaction = db . transaction ( [ storeName ] , 'readwrite' ) ;
129+ const objectStore = transaction . objectStore ( storeName ) ;
130+
131+ const putRequest = objectStore . put ( myData ) ;
132+
133+ putRequest . onsuccess = ( ) => {
134+ this . setState ( { success : '成功:数据已存入,且符合 keyPath 要求!' } ) ;
135+ } ;
136+ putRequest . onerror = ( e ) => {
137+ this . setState ( { error : `写入失败: ${ e . target . error } ` } ) ;
138+ } ;
139+
140+ transaction . oncomplete = ( ) => {
141+ db . close ( ) ;
142+ } ;
143+ } ;
144+
145+ request . onerror = ( e ) => {
146+ this . setState ( { error : `打开数据库失败: ${ e . target . error } ` } ) ;
147+ } ;
148+
149+ request . onupgradeneeded = ( event ) => {
150+ // 如果数据库或对象存储不存在,先创建
151+ const db = event . target . result ;
152+ if ( ! db . objectStoreNames . contains ( storeName ) ) {
153+ db . createObjectStore ( storeName , { keyPath : 'id' } ) ;
154+ }
155+ } ;
156+ } catch ( e ) {
157+ this . setState ( { error : `操作失败: ${ e . message } ` } ) ;
158+ }
159+ }
160+
161+ render ( ) {
162+ if ( ! this . props . visible ) {
163+ return null ;
164+ }
165+
166+ return (
167+ < DebugWindowComponent
168+ value = { this . state . value }
169+ error = { this . state . error }
170+ success = { this . state . success }
171+ onCancel = { this . handleCancel }
172+ onChange = { this . handleChange }
173+ onOk = { this . handleOk }
174+ />
175+ ) ;
176+ }
177+ }
178+
179+ DebugWindow . propTypes = {
180+ visible : PropTypes . bool . isRequired ,
181+ onToggleDebugWindow : PropTypes . func . isRequired
182+ } ;
183+
184+ const mapStateToProps = state => ( {
185+ // visible is set by parent component via prop
186+ } ) ;
187+
188+ const mapDispatchToProps = ( ) => ( { } ) ;
189+
190+ export default connect (
191+ mapStateToProps ,
192+ mapDispatchToProps
193+ ) ( DebugWindow ) ;
0 commit comments