Skip to content

Commit e829433

Browse files
committed
加入调试载入书包内容
1 parent cf96cd3 commit e829433

4 files changed

Lines changed: 363 additions & 0 deletions

File tree

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
.modalContent {
2+
width: 600px;
3+
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
4+
}
5+
6+
.body {
7+
padding: 20px;
8+
}
9+
10+
.title {
11+
font-size: 1.2rem;
12+
font-weight: 600;
13+
margin: 0 0 10px 0;
14+
color: #333;
15+
}
16+
17+
.helpText {
18+
font-size: 0.9rem;
19+
color: #666;
20+
margin: 0 0 12px 0;
21+
line-height: 1.4;
22+
}
23+
24+
.textArea {
25+
width: 100%;
26+
font-family: "SF Mono", "Monaco", "Inconsolata", "Fira Mono", "Droid Sans Mono", "Source Code Pro", monospace;
27+
font-size: 0.85rem;
28+
padding: 10px;
29+
border: 1px solid #ccc;
30+
border-radius: 6px;
31+
resize: vertical;
32+
box-sizing: border-box;
33+
line-height: 1.4;
34+
}
35+
36+
.textArea:focus {
37+
outline: none;
38+
border-color: #4c97ff;
39+
box-shadow: 0 0 0 3px rgba(76, 151, 255, 0.2);
40+
}
41+
42+
.errorArea {
43+
min-height: 40px;
44+
margin: 8px 0;
45+
}
46+
47+
.errorText {
48+
color: #d32f2f;
49+
font-size: 0.85rem;
50+
margin: 4px 0;
51+
white-space: pre-wrap;
52+
word-break: break-all;
53+
}
54+
55+
.successText {
56+
color: #2e7d32;
57+
font-size: 0.85rem;
58+
margin: 4px 0;
59+
white-space: pre-wrap;
60+
word-break: break-all;
61+
}
62+
63+
.buttonRow {
64+
display: flex;
65+
justify-content: flex-end;
66+
gap: 10px;
67+
margin-top: 10px;
68+
}
69+
70+
.cancelButton {
71+
padding: 8px 20px;
72+
border: 1px solid #ccc;
73+
border-radius: 6px;
74+
background: #fff;
75+
color: #333;
76+
font-size: 0.9rem;
77+
cursor: pointer;
78+
transition: background 0.15s;
79+
}
80+
81+
.cancelButton:hover {
82+
background: #f5f5f5;
83+
}
84+
85+
.okButton {
86+
padding: 8px 20px;
87+
border: 1px solid #4c97ff;
88+
border-radius: 6px;
89+
background: #4c97ff;
90+
color: #fff;
91+
font-size: 0.9rem;
92+
cursor: pointer;
93+
transition: background 0.15s;
94+
}
95+
96+
.okButton:hover {
97+
background: #3a7bd5;
98+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import Box from '../box/box.jsx';
4+
import Modal from '../../containers/modal.jsx';
5+
6+
import styles from './debug-window.css';
7+
8+
const DebugWindowComponent = props => (
9+
<Modal
10+
className={styles.modalContent}
11+
onRequestClose={props.onCancel}
12+
contentLabel="调试窗口"
13+
id="debugWindow"
14+
>
15+
<Box className={styles.body}>
16+
<h2 className={styles.title}>IndexedDB 调试工具</h2>
17+
<p className={styles.helpText}>
18+
输入要存入 IndexedDB (TW_Backpack / backpack) 的数据 (JSON 格式):
19+
</p>
20+
<textarea
21+
autoFocus
22+
className={styles.textArea}
23+
value={props.value}
24+
onChange={props.onChange}
25+
rows={12}
26+
spellCheck="false"
27+
/>
28+
<div className={styles.errorArea}>
29+
{props.error ? (
30+
<p className={styles.errorText}>{props.error}</p>
31+
) : null}
32+
{props.success ? (
33+
<p className={styles.successText}>{props.success}</p>
34+
) : null}
35+
</div>
36+
<Box className={styles.buttonRow}>
37+
<button
38+
className={styles.cancelButton}
39+
onClick={props.onCancel}
40+
>
41+
关闭
42+
</button>
43+
<button
44+
className={styles.okButton}
45+
onClick={props.onOk}
46+
>
47+
执行写入
48+
</button>
49+
</Box>
50+
</Box>
51+
</Modal>
52+
);
53+
54+
DebugWindowComponent.propTypes = {
55+
value: PropTypes.string.isRequired,
56+
error: PropTypes.string,
57+
success: PropTypes.string,
58+
onCancel: PropTypes.func.isRequired,
59+
onChange: PropTypes.func.isRequired,
60+
onOk: PropTypes.func.isRequired
61+
};
62+
63+
export default DebugWindowComponent;

src/components/gui/gui.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import TWGitModal from '../../containers/tw-git-modal.jsx';
4949
import TWToolboxLayoutModal from '../../containers/tw-toolbox-layout-modal.jsx';
5050
import SpriteLayerModal from '../../containers/sprite-layer-modal.jsx';
5151
import CollaborationContainer from '../../containers/collaboration-container.jsx';
52+
import DebugWindow from '../../containers/debug-window.jsx';
5253

5354
import {STAGE_SIZE_MODES, FIXED_WIDTH, UNCONSTRAINED_NON_STAGE_WIDTH} from '../../lib/layout-constants';
5455
import {resolveStageSize} from '../../lib/screen-utils';
@@ -354,6 +355,10 @@ const GUIComponent = props => {
354355
const [targetPaneWindowSize, setTargetPaneWindowSize] = React.useState({width: 485, height: 447});
355356
const [targetPaneWindowMinimized, setTargetPaneWindowMinimized] = React.useState(false);
356357
const [menuBarCollapsed, setMenuBarCollapsed] = React.useState(false);
358+
const [debugWindowVisible, setDebugWindowVisible] = React.useState(false);
359+
const handleToggleDebugWindow = React.useCallback(() => {
360+
setDebugWindowVisible(prev => !prev);
361+
}, []);
357362
const [editorWindowSessions, setEditorWindowSessions] = React.useState([]);
358363
const [activeEditorWindowId, setActiveEditorWindowId] = React.useState(null);
359364
const editorWindowSessionsRef = React.useRef(editorWindowSessions);
@@ -1578,6 +1583,10 @@ const GUIComponent = props => {
15781583
visible={collaborationModalVisible}
15791584
onRequestClose={onRequestCloseCollaborationModal}
15801585
/>
1586+
<DebugWindow
1587+
visible={debugWindowVisible}
1588+
onToggleDebugWindow={handleToggleDebugWindow}
1589+
/>
15811590
</React.Fragment>
15821591
);
15831592

src/containers/debug-window.jsx

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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

Comments
 (0)