hanli/web/index.html
2025-12-01 18:13:15 +08:00

505 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>凡人修仙传 - 文字版</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #1a1a2e;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: 'Consolas', 'Monaco', monospace;
}
.container {
width: 100%;
max-width: 900px;
padding: 20px;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.header h1 {
color: #eee;
font-size: 24px;
margin-bottom: 10px;
}
.header p {
color: #888;
font-size: 14px;
}
/* 登录界面 */
#auth-panel {
background: #16213e;
border-radius: 10px;
padding: 30px;
max-width: 400px;
margin: 0 auto;
}
#auth-panel h2 {
color: #eee;
text-align: center;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
color: #aaa;
margin-bottom: 5px;
font-size: 14px;
}
.form-group input {
width: 100%;
padding: 10px 15px;
border: 1px solid #0f3460;
border-radius: 5px;
background: #1a1a2e;
color: #eee;
font-size: 16px;
}
.form-group input:focus {
outline: none;
border-color: #e94560;
}
.btn-group {
display: flex;
gap: 10px;
margin-top: 20px;
}
.btn {
flex: 1;
padding: 12px;
border: none;
border-radius: 5px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background: #e94560;
color: white;
}
.btn-primary:hover {
background: #ff6b6b;
}
.btn-secondary {
background: #0f3460;
color: white;
}
.btn-secondary:hover {
background: #16213e;
}
.message {
padding: 10px;
border-radius: 5px;
margin-bottom: 15px;
text-align: center;
display: none;
}
.message.error {
background: rgba(233, 69, 96, 0.2);
color: #e94560;
display: block;
}
.message.success {
background: rgba(46, 213, 115, 0.2);
color: #2ed573;
display: block;
}
/* 游戏终端 */
#game-panel {
display: none;
}
.terminal-header {
background: #16213e;
padding: 10px 15px;
border-radius: 10px 10px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.terminal-header .user-info {
color: #2ed573;
font-size: 14px;
}
.terminal-header .logout-btn {
background: #e94560;
color: white;
border: none;
padding: 5px 15px;
border-radius: 5px;
cursor: pointer;
font-size: 12px;
}
.terminal-header .logout-btn:hover {
background: #ff6b6b;
}
#terminal {
background: #0d0d0d;
border-radius: 0 0 10px 10px;
padding: 10px;
}
.input-area {
background: #16213e;
padding: 10px;
border-radius: 0 0 10px 10px;
display: flex;
gap: 10px;
}
.input-area input {
flex: 1;
padding: 10px;
border: 1px solid #0f3460;
border-radius: 5px;
background: #1a1a2e;
color: #eee;
font-size: 16px;
}
.input-area input:focus {
outline: none;
border-color: #e94560;
}
.input-area button {
padding: 10px 20px;
background: #e94560;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
.quick-buttons {
display: flex;
flex-wrap: wrap;
gap: 5px;
margin-top: 10px;
}
.quick-btn {
padding: 8px 15px;
background: #0f3460;
color: #eee;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
}
.quick-btn:hover {
background: #16213e;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>凡人修仙传 - 文字版</h1>
<p>Web Terminal Edition</p>
</div>
<!-- 登录/注册面板 -->
<div id="auth-panel">
<h2>欢迎</h2>
<div id="auth-message" class="message"></div>
<div class="form-group">
<label>用户名</label>
<input type="text" id="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<label>密码</label>
<input type="password" id="password" placeholder="请输入密码">
</div>
<div class="btn-group">
<button class="btn btn-primary" onclick="login()">登录</button>
<button class="btn btn-secondary" onclick="register()">注册</button>
</div>
</div>
<!-- 游戏面板 -->
<div id="game-panel">
<div class="terminal-header">
<span class="user-info">玩家: <span id="player-name">-</span></span>
<button class="logout-btn" onclick="logout()">退出登录</button>
</div>
<div id="terminal"></div>
<div class="input-area">
<input type="text" id="game-input" placeholder="输入命令..." onkeypress="handleKeyPress(event)">
<button onclick="sendInput()">发送</button>
</div>
<div class="quick-buttons">
<button class="quick-btn" onclick="quickSend('1')">1.战斗</button>
<button class="quick-btn" onclick="quickSend('2')">2.属性</button>
<button class="quick-btn" onclick="quickSend('3')">3.背包</button>
<button class="quick-btn" onclick="quickSend('4')">4.故人</button>
<button class="quick-btn" onclick="quickSend('5')">5.同伴</button>
<button class="quick-btn" onclick="quickSend('6')">6.天赋</button>
<button class="quick-btn" onclick="quickSend('7')">7.地图</button>
<button class="quick-btn" onclick="quickSend('8')">8.休息</button>
<button class="quick-btn" onclick="quickSend('0')">0.返回</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.min.js"></script>
<script>
let terminal = null;
let fitAddon = null;
let isLoggedIn = false;
// 初始化终端
function initTerminal() {
terminal = new Terminal({
cursorBlink: true,
fontSize: 16,
fontFamily: 'Consolas, Monaco, monospace',
theme: {
background: '#0d0d0d',
foreground: '#eee',
cursor: '#e94560',
cursorAccent: '#0d0d0d',
selection: 'rgba(233, 69, 96, 0.3)',
black: '#1a1a2e',
red: '#e94560',
green: '#2ed573',
yellow: '#ffa502',
blue: '#70a1ff',
magenta: '#ff6b81',
cyan: '#1e90ff',
white: '#eee',
brightBlack: '#666',
brightRed: '#ff6b6b',
brightGreen: '#7bed9f',
brightYellow: '#ffda79',
brightBlue: '#a4b0be',
brightMagenta: '#ff7f9f',
brightCyan: '#34ace0',
brightWhite: '#fff'
},
rows: 24,
cols: 80
});
fitAddon = new FitAddon.FitAddon();
terminal.loadAddon(fitAddon);
terminal.open(document.getElementById('terminal'));
fitAddon.fit();
window.addEventListener('resize', () => {
fitAddon.fit();
});
}
// API 请求
async function api(endpoint, data = {}) {
try {
const response = await fetch('/api/' + endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data),
credentials: 'include'
});
return await response.json();
} catch (error) {
console.error('API Error:', error);
return { success: false, message: '网络错误' };
}
}
// 显示消息
function showMessage(message, type = 'error') {
const el = document.getElementById('auth-message');
el.textContent = message;
el.className = 'message ' + type;
}
// 登录
async function login() {
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value;
if (!username || !password) {
showMessage('请输入用户名和密码');
return;
}
const result = await api('login', { username, password });
if (result.success) {
showMessage('登录成功!', 'success');
setTimeout(() => {
enterGame(username);
}, 500);
} else {
showMessage(result.message || '登录失败');
}
}
// 注册
async function register() {
const username = document.getElementById('username').value.trim();
const password = document.getElementById('password').value;
if (!username || !password) {
showMessage('请输入用户名和密码');
return;
}
const result = await api('register', { username, password });
if (result.success) {
showMessage('注册成功!自动登录中...', 'success');
setTimeout(() => {
enterGame(username);
}, 500);
} else {
showMessage(result.message || '注册失败');
}
}
// 退出登录
async function logout() {
await api('logout');
isLoggedIn = false;
document.getElementById('auth-panel').style.display = 'block';
document.getElementById('game-panel').style.display = 'none';
document.getElementById('password').value = '';
showMessage('', '');
}
// 进入游戏
function enterGame(username) {
isLoggedIn = true;
document.getElementById('auth-panel').style.display = 'none';
document.getElementById('game-panel').style.display = 'block';
document.getElementById('player-name').textContent = username;
if (!terminal) {
initTerminal();
}
terminal.clear();
terminal.writeln('\x1b[33m正在加载游戏...\x1b[0m');
// 渲染游戏界面
renderGame();
// 聚焦输入框
document.getElementById('game-input').focus();
}
// 渲染游戏
async function renderGame() {
const result = await api('game/render');
if (result.success) {
terminal.clear();
const lines = result.output.split('\n');
lines.forEach(line => {
terminal.writeln(line);
});
} else {
terminal.writeln('\x1b[31m' + (result.message || '加载失败') + '\x1b[0m');
if (result.message === '请先登录') {
logout();
}
}
}
// 发送输入
async function sendInput() {
const inputEl = document.getElementById('game-input');
const input = inputEl.value.trim();
inputEl.value = '';
if (!input) return;
terminal.writeln('\x1b[36m> ' + input + '\x1b[0m');
terminal.writeln('');
const result = await api('game/input', { input });
if (result.success) {
terminal.clear();
const lines = result.output.split('\n');
lines.forEach(line => {
terminal.writeln(line);
});
} else {
terminal.writeln('\x1b[31m' + (result.message || '操作失败') + '\x1b[0m');
if (result.message === '请先登录') {
logout();
}
}
inputEl.focus();
}
// 快捷按钮发送
function quickSend(value) {
document.getElementById('game-input').value = value;
sendInput();
}
// 按键处理
function handleKeyPress(event) {
if (event.key === 'Enter') {
sendInput();
}
}
// 页面加载时检查登录状态
window.onload = async function() {
const result = await api('status');
if (result.success && result.loggedIn) {
enterGame(result.username);
}
};
</script>
</body>
</html>