Skip to content

Commit f5b29a9

Browse files
committed
feat: 添加输入面板,支持自定义课程数量和先修关系
1 parent 4279083 commit f5b29a9

File tree

3 files changed

+178
-16
lines changed

3 files changed

+178
-16
lines changed

src/components/App.tsx

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { useMemo } from 'react';
1+
import { useMemo, useState, useCallback } from 'react';
22
import Header from './Header';
3+
import InputPanel from './InputPanel';
34
import CodePanel from './CodePanel';
45
import GraphView from './GraphView';
56
import DataStructuresPanel from './DataStructuresPanel';
@@ -11,36 +12,43 @@ import { GraphNode, GraphEdge } from '../algorithm/types';
1112
import './App.css';
1213

1314
// Default example input
14-
const DEFAULT_INPUT = {
15-
numCourses: 4,
16-
prerequisites: [
17-
[1, 0],
18-
[2, 0],
19-
[3, 1],
20-
[3, 2],
21-
],
22-
};
15+
const DEFAULT_NUM_COURSES = 4;
16+
const DEFAULT_PREREQUISITES: number[][] = [
17+
[1, 0],
18+
[2, 0],
19+
[3, 1],
20+
[3, 2],
21+
];
2322

2423
function App() {
24+
const [numCourses, setNumCourses] = useState(DEFAULT_NUM_COURSES);
25+
const [prerequisites, setPrerequisites] = useState(DEFAULT_PREREQUISITES);
26+
2527
// Generate algorithm steps
2628
const steps = useMemo(
27-
() => generateSteps(DEFAULT_INPUT),
28-
[]
29+
() => generateSteps({ numCourses, prerequisites }),
30+
[numCourses, prerequisites]
2931
);
3032

3133
// Build graph data
3234
const graphData = useMemo(() => {
3335
const nodes: GraphNode[] = [];
34-
for (let i = 0; i < DEFAULT_INPUT.numCourses; i++) {
36+
for (let i = 0; i < numCourses; i++) {
3537
nodes.push({ id: i });
3638
}
3739

38-
const edges: GraphEdge[] = DEFAULT_INPUT.prerequisites.map(([from, to]) => ({
40+
const edges: GraphEdge[] = prerequisites.map(([from, to]) => ({
3941
source: from,
4042
target: to,
4143
}));
4244

4345
return { nodes, edges };
46+
}, [numCourses, prerequisites]);
47+
48+
// Handle input change
49+
const handleInputSubmit = useCallback((newNumCourses: number, newPrerequisites: number[][]) => {
50+
setNumCourses(newNumCourses);
51+
setPrerequisites(newPrerequisites);
4452
}, []);
4553

4654
// Playback state
@@ -78,6 +86,12 @@ function App() {
7886
githubUrl="https://github.com/fuck-algorithm/leetcode-207-course-schedule"
7987
/>
8088

89+
<InputPanel
90+
onSubmit={handleInputSubmit}
91+
defaultNumCourses={DEFAULT_NUM_COURSES}
92+
defaultPrerequisites={DEFAULT_PREREQUISITES}
93+
/>
94+
8195
<main className="main-content">
8296
<section className="code-section">
8397
<div className="section-title">算法代码</div>
@@ -101,7 +115,7 @@ function App() {
101115
queue={currentStep.queue}
102116
stepDescription={currentStep.description}
103117
learnCount={currentStep.learnCount}
104-
numCourses={DEFAULT_INPUT.numCourses}
118+
numCourses={numCourses}
105119
/>
106120
</div>
107121
</section>
@@ -113,7 +127,7 @@ function App() {
113127
inDegree={currentStep.inDegree}
114128
queue={currentStep.queue}
115129
learnCount={currentStep.learnCount}
116-
numCourses={DEFAULT_INPUT.numCourses}
130+
numCourses={numCourses}
117131
highlightedIndex={null}
118132
isComplete={isComplete}
119133
/>

src/components/InputPanel.css

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
.input-panel {
2+
display: flex;
3+
align-items: center;
4+
gap: 16px;
5+
padding: 12px 20px;
6+
background: #1e1e1e;
7+
border-bottom: 1px solid #333;
8+
flex-wrap: wrap;
9+
}
10+
11+
.input-group {
12+
display: flex;
13+
align-items: center;
14+
gap: 8px;
15+
}
16+
17+
.input-group label {
18+
color: #9cdcfe;
19+
font-size: 14px;
20+
white-space: nowrap;
21+
}
22+
23+
.input-group input {
24+
padding: 6px 10px;
25+
border: 1px solid #444;
26+
border-radius: 4px;
27+
background: #2d2d2d;
28+
color: #d4d4d4;
29+
font-size: 14px;
30+
font-family: 'Consolas', 'Monaco', monospace;
31+
}
32+
33+
.input-group input[type="number"] {
34+
width: 60px;
35+
}
36+
37+
.input-group input[type="text"] {
38+
width: 280px;
39+
}
40+
41+
.input-group input:focus {
42+
outline: none;
43+
border-color: #007acc;
44+
}
45+
46+
.input-error {
47+
color: #f48771;
48+
font-size: 13px;
49+
}
50+
51+
.run-button {
52+
padding: 6px 16px;
53+
background: #0e639c;
54+
color: white;
55+
border: none;
56+
border-radius: 4px;
57+
cursor: pointer;
58+
font-size: 14px;
59+
transition: background 0.2s;
60+
}
61+
62+
.run-button:hover {
63+
background: #1177bb;
64+
}
65+
66+
.run-button:active {
67+
background: #0d5a8c;
68+
}

src/components/InputPanel.tsx

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { useState } from 'react';
2+
import './InputPanel.css';
3+
4+
interface InputPanelProps {
5+
onSubmit: (numCourses: number, prerequisites: number[][]) => void;
6+
defaultNumCourses: number;
7+
defaultPrerequisites: number[][];
8+
}
9+
10+
export default function InputPanel({
11+
onSubmit,
12+
defaultNumCourses,
13+
defaultPrerequisites,
14+
}: InputPanelProps) {
15+
const [numCourses, setNumCourses] = useState(defaultNumCourses.toString());
16+
const [prerequisites, setPrerequisites] = useState(
17+
JSON.stringify(defaultPrerequisites)
18+
);
19+
const [error, setError] = useState<string | null>(null);
20+
21+
const handleSubmit = () => {
22+
try {
23+
const num = parseInt(numCourses, 10);
24+
if (isNaN(num) || num < 1 || num > 20) {
25+
setError('课程数量必须是 1-20 之间的整数');
26+
return;
27+
}
28+
29+
const prereqs = JSON.parse(prerequisites);
30+
if (!Array.isArray(prereqs)) {
31+
setError('先修课程必须是数组格式');
32+
return;
33+
}
34+
35+
for (const pair of prereqs) {
36+
if (!Array.isArray(pair) || pair.length !== 2) {
37+
setError('每个先修关系必须是 [a, b] 格式');
38+
return;
39+
}
40+
if (pair[0] < 0 || pair[0] >= num || pair[1] < 0 || pair[1] >= num) {
41+
setError(`课程编号必须在 0 到 ${num - 1} 之间`);
42+
return;
43+
}
44+
}
45+
46+
setError(null);
47+
onSubmit(num, prereqs);
48+
} catch {
49+
setError('先修课程格式错误,请使用 JSON 数组格式');
50+
}
51+
};
52+
53+
return (
54+
<div className="input-panel">
55+
<div className="input-group">
56+
<label>课程数量 (numCourses):</label>
57+
<input
58+
type="number"
59+
value={numCourses}
60+
onChange={(e) => setNumCourses(e.target.value)}
61+
min="1"
62+
max="20"
63+
/>
64+
</div>
65+
<div className="input-group">
66+
<label>先修课程 (prerequisites):</label>
67+
<input
68+
type="text"
69+
value={prerequisites}
70+
onChange={(e) => setPrerequisites(e.target.value)}
71+
placeholder="[[1,0],[2,1]]"
72+
/>
73+
</div>
74+
{error && <div className="input-error">{error}</div>}
75+
<button className="run-button" onClick={handleSubmit}>
76+
运行
77+
</button>
78+
</div>
79+
);
80+
}

0 commit comments

Comments
 (0)