mirror of
https://github.com/LHY0125/Gobang-Game.git
synced 2026-06-28 16:35:55 +08:00
feat(frontend): App 路由集成 + 木纹风格 CSS
App.tsx 添加 menu/game/replay 三页面路由切换,MainMenu 新增 onReplayStart 属性区分对局与回放入口。App.css 实现经典木纹 视觉风格(深棕底色、米黄文字、皮革纹理按钮),index.css 基础 重置。修复 tsconfig 中 erasableSyntaxOnly 无效选项并安装 @types/node。
This commit is contained in:
Generated
+18
@@ -18,6 +18,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2.0.0",
|
"@tauri-apps/cli": "^2.0.0",
|
||||||
|
"@types/node": "^25.9.1",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-dom": "^19.0.0",
|
"@types/react-dom": "^19.0.0",
|
||||||
"@vitejs/plugin-react": "^4.4.0",
|
"@vitejs/plugin-react": "^4.4.0",
|
||||||
@@ -1472,6 +1473,16 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "25.9.1",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/node/-/node-25.9.1.tgz",
|
||||||
|
"integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": ">=7.24.0 <7.24.7"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/react": {
|
"node_modules/@types/react": {
|
||||||
"version": "19.2.15",
|
"version": "19.2.15",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/react/-/react-19.2.15.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/react/-/react-19.2.15.tgz",
|
||||||
@@ -2360,6 +2371,13 @@
|
|||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.24.6",
|
||||||
|
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.24.6.tgz",
|
||||||
|
"integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
"resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
|
||||||
|
|||||||
+7
-6
@@ -11,21 +11,22 @@
|
|||||||
"test:watch": "vitest --watch"
|
"test:watch": "vitest --watch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2.0.0",
|
||||||
|
"@tauri-apps/plugin-opener": "^2.0.0",
|
||||||
|
"i18next": "^24.0.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"zustand": "^5.0.0",
|
|
||||||
"i18next": "^24.0.0",
|
|
||||||
"react-i18next": "^15.0.0",
|
"react-i18next": "^15.0.0",
|
||||||
"@tauri-apps/api": "^2.0.0",
|
"zustand": "^5.0.0"
|
||||||
"@tauri-apps/plugin-opener": "^2.0.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tauri-apps/cli": "^2.0.0",
|
||||||
|
"@types/node": "^25.9.1",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-dom": "^19.0.0",
|
"@types/react-dom": "^19.0.0",
|
||||||
"@vitejs/plugin-react": "^4.4.0",
|
"@vitejs/plugin-react": "^4.4.0",
|
||||||
"typescript": "~5.7.0",
|
"typescript": "~5.7.0",
|
||||||
"vite": "^6.0.0",
|
"vite": "^6.0.0",
|
||||||
"vitest": "^3.0.0",
|
"vitest": "^3.0.0"
|
||||||
"@tauri-apps/cli": "^2.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+183
@@ -1,7 +1,190 @@
|
|||||||
|
:root {
|
||||||
|
--bg-primary: #3C2415;
|
||||||
|
--bg-secondary: #F5DEB3;
|
||||||
|
--text-primary: #F5DEB3;
|
||||||
|
--text-secondary: #3C2415;
|
||||||
|
--accent: #8B4513;
|
||||||
|
--accent-hover: #A0522D;
|
||||||
|
--button-bg: #DEB887;
|
||||||
|
--button-hover: #D2B48C;
|
||||||
|
--border: #8B7355;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Microsoft YaHei', sans-serif;
|
||||||
|
background-color: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
.app {
|
.app {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
gap: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-title {
|
||||||
|
font-size: 42px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-buttons button {
|
||||||
|
width: 240px;
|
||||||
|
padding: 14px 28px;
|
||||||
|
font-size: 18px;
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-buttons button:hover {
|
||||||
|
background: var(--button-hover);
|
||||||
|
transform: scale(1.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100vh;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel h2 {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel label {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-panel select, .setup-panel input {
|
||||||
|
padding: 6px 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 10px 24px;
|
||||||
|
font-size: 16px;
|
||||||
|
border: 2px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--button-bg);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background: var(--button-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 12px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.board-container {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-info {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timer-display {
|
||||||
|
font-size: 24px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timer-warning {
|
||||||
|
color: #ff4444;
|
||||||
|
animation: blink 0.5s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blink {
|
||||||
|
from { opacity: 1; }
|
||||||
|
to { opacity: 0.3; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.replay-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
padding: 12px;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step-slider {
|
||||||
|
width: 80%;
|
||||||
|
accent-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.replay-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-3
@@ -1,8 +1,26 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import MainMenu from './components/menu/MainMenu';
|
||||||
|
import GameView from './components/game/GameView';
|
||||||
|
import ReplayView from './components/replay/ReplayView';
|
||||||
|
import './App.css';
|
||||||
|
|
||||||
|
type Page = 'menu' | 'game' | 'replay';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [page, setPage] = useState<Page>('menu');
|
||||||
|
|
||||||
|
const handleGameStart = () => setPage('game');
|
||||||
|
const handleReplayStart = () => setPage('replay');
|
||||||
|
const handleBackToMenu = () => setPage('menu');
|
||||||
|
|
||||||
|
if (page === 'game') return <GameView onBackToMenu={handleBackToMenu} />;
|
||||||
|
if (page === 'replay') return <ReplayView onBackToMenu={handleBackToMenu} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<MainMenu
|
||||||
<h1>五子棋 v2.0</h1>
|
onGameStart={handleGameStart}
|
||||||
</div>
|
onReplayStart={handleReplayStart}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,16 +9,17 @@ type View = 'main' | 'local' | 'ai' | 'online' | 'replay';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onGameStart: () => void;
|
onGameStart: () => void;
|
||||||
|
onReplayStart: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function MainMenu({ onGameStart }: Props) {
|
export default function MainMenu({ onGameStart, onReplayStart }: Props) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [view, setView] = useState<View>('main');
|
const [view, setView] = useState<View>('main');
|
||||||
|
|
||||||
if (view === 'local') return <LocalGameSetup onBack={() => setView('main')} onStart={onGameStart} />;
|
if (view === 'local') return <LocalGameSetup onBack={() => setView('main')} onStart={onGameStart} />;
|
||||||
if (view === 'ai') return <AiGameSetup onBack={() => setView('main')} onStart={onGameStart} />;
|
if (view === 'ai') return <AiGameSetup onBack={() => setView('main')} onStart={onGameStart} />;
|
||||||
if (view === 'online') return <OnlineSetup onBack={() => setView('main')} onStart={onGameStart} />;
|
if (view === 'online') return <OnlineSetup onBack={() => setView('main')} onStart={onGameStart} />;
|
||||||
if (view === 'replay') return <LoadReplay onBack={() => setView('main')} onStart={onGameStart} />;
|
if (view === 'replay') return <LoadReplay onBack={() => setView('main')} onStart={onReplayStart} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="main-menu">
|
<div className="main-menu">
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
/* Linting */
|
/* Linting */
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"erasableSyntaxOnly": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": ["vite.config.ts"]
|
"include": ["vite.config.ts"]
|
||||||
|
|||||||
Reference in New Issue
Block a user