Files
springboot-scaffold/target/classes/static/users.html
likingcode c04235c655 feat: Spring Boot 学习脚手架 v2.0
- 新增 IoC 容器学习模块
- 新增 AOP 切面编程学习模块
- 新增 MyBatis 集成学习模块
- 新增事务管理学习模块
- 新增用户/产品/订单 CRUD
- 新增 7 个交互式学习页面
- 集成性能监控切面
2026-03-07 08:37:40 +00:00

278 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户管理 - Spring Boot CRUD</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f5f5f5; }
.container { max-width: 1400px; margin: 0 auto; padding: 20px; }
.header { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); color: white; padding: 30px 20px; text-align: center; margin-bottom: 20px; border-radius: 10px; }
.header h1 { font-size: 2em; }
.nav { display: flex; gap: 10px; margin-bottom: 20px; flex-wrap: wrap; justify-content: center; }
.nav a { padding: 10px 20px; background: white; border-radius: 20px; text-decoration: none; color: #333; font-size: 0.9em; }
.nav a:hover, .nav a.active { background: #11998e; color: white; }
.card { background: white; border-radius: 10px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 10px rgba(0,0,0,0.08); }
.card h3 { color: #11998e; margin-bottom: 15px; border-bottom: 2px solid #eee; padding-bottom: 10px; }
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 15px; margin-bottom: 20px; }
.form-group { display: flex; flex-direction: column; }
.form-group label { margin-bottom: 5px; color: #666; font-size: 0.9em; }
.form-group input, .form-group textarea { padding: 10px; border: 1px solid #ddd; border-radius: 5px; font-size: 1em; }
.form-group input:focus, .form-group textarea:focus { outline: none; border-color: #11998e; }
.btn { padding: 10px 20px; border: none; border-radius: 5px; cursor: pointer; font-size: 1em; margin: 5px; }
.btn-primary { background: #11998e; color: white; }
.btn-primary:hover { background: #0d7a6e; }
.btn-secondary { background: #6c757d; color: white; }
.btn-danger { background: #dc3545; color: white; }
.btn-warning { background: #ffc107; color: #333; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; }
th { background: #f8f9fa; font-weight: 600; }
tr:hover { background: #f8f9fa; }
.actions { display: flex; gap: 5px; }
.actions button { padding: 5px 10px; font-size: 0.85em; }
.badge { padding: 4px 8px; border-radius: 4px; font-size: 0.8em; }
.badge-active { background: #28a745; color: white; }
.badge-inactive { background: #6c757d; color: white; }
.search-box { display: flex; gap: 10px; margin-bottom: 20px; }
.search-box input { flex: 1; padding: 10px; border: 1px solid #ddd; border-radius: 5px; }
.result { background: #1e1e1e; color: #d4d4d4; padding: 15px; border-radius: 5px; margin-top: 10px; font-family: monospace; font-size: 0.9em; overflow-x: auto; max-height: 300px; overflow-y: auto; }
.result.success { background: #d4edda; color: #155724; }
.result.error { background: #f8d7da; color: #721c24; }
.modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; }
.modal-content { background: white; margin: 50px auto; padding: 30px; width: 90%; max-width: 600px; border-radius: 10px; }
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
.modal-header h3 { margin: 0; border: none; }
.close { font-size: 1.5em; cursor: pointer; color: #999; }
.close:hover { color: #333; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>👥 用户管理</h1>
<p>RESTful CRUD 操作演示</p>
</div>
<div class="nav">
<a href="index.html">🏠 首页</a>
<a href="ioc.html">📦 IoC</a>
<a href="aop.html">🔪 AOP</a>
<a href="mybatis.html">💾 MyBatis</a>
<a href="transaction.html">🔄 事务</a>
<a href="users.html" class="active">👥 用户</a>
<a href="api.html">🔌 API</a>
</div>
<div class="card">
<h3> 创建/编辑用户</h3>
<form id="userForm">
<input type="hidden" id="userId">
<div class="form-grid">
<div class="form-group">
<label>用户名 *</label>
<input type="text" id="username" required placeholder="输入用户名">
</div>
<div class="form-group">
<label>邮箱 *</label>
<input type="email" id="email" required placeholder="输入邮箱">
</div>
<div class="form-group">
<label>手机号</label>
<input type="text" id="phone" placeholder="输入手机号">
</div>
<div class="form-group">
<label>状态</label>
<select id="active" style="padding:10px;border:1px solid #ddd;border-radius:5px;">
<option value="true">启用</option>
<option value="false">禁用</option>
</select>
</div>
</div>
<div class="form-group" style="margin-bottom:15px;">
<label>简介</label>
<textarea id="bio" rows="3" placeholder="输入个人简介"></textarea>
</div>
<button type="submit" class="btn btn-primary">💾 保存</button>
<button type="button" class="btn btn-secondary" onclick="resetForm()">🔄 重置</button>
</form>
<div id="formResult"></div>
</div>
<div class="card">
<h3>📋 用户列表</h3>
<div class="search-box">
<input type="text" id="searchInput" placeholder="搜索用户名...">
<button class="btn btn-primary" onclick="searchUsers()">🔍 搜索</button>
<button class="btn btn-secondary" onclick="loadUsers()">🔄 刷新</button>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>邮箱</th>
<th>手机号</th>
<th>状态</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="userTable">
<tr><td colspan="7" style="text-align:center;color:#999;">加载中...</td></tr>
</tbody>
</table>
</div>
</div>
<script>
let users = [];
async function loadUsers() {
const tbody = document.getElementById('userTable');
tbody.innerHTML = '<tr><td colspan="7" style="text-align:center;color:#999;">加载中...</td></tr>';
try {
const res = await fetch('/api/users');
users = await res.json();
renderUsers(users);
} catch (e) {
tbody.innerHTML = `<tr><td colspan="7" style="text-align:center;color:#dc3545;">加载失败: ${e.message}</td></tr>`;
}
}
function renderUsers(data) {
const tbody = document.getElementById('userTable');
if (data.length === 0) {
tbody.innerHTML = '<tr><td colspan="7" style="text-align:center;color:#999;">暂无数据</td></tr>';
return;
}
tbody.innerHTML = data.map(u => `
<tr>
<td>${u.id}</td>
<td>${u.username}</td>
<td>${u.email}</td>
<td>${u.phone || '-'}</td>
<td><span class="badge ${u.active ? 'badge-active' : 'badge-inactive'}">${u.active ? '启用' : '禁用'}</span></td>
<td>${u.createdAt ? new Date(u.createdAt).toLocaleString() : '-'}</td>
<td class="actions">
<button class="btn btn-warning" onclick="editUser(${u.id})">✏️ 编辑</button>
<button class="btn btn-danger" onclick="deleteUser(${u.id})">🗑️ 删除</button>
</td>
</tr>
`).join('');
}
async function searchUsers() {
const keyword = document.getElementById('searchInput').value.trim();
if (!keyword) {
loadUsers();
return;
}
try {
const res = await fetch(`/api/users/search?username=${encodeURIComponent(keyword)}`);
const data = await res.json();
renderUsers(data);
} catch (e) {
alert('搜索失败: ' + e.message);
}
}
document.getElementById('userForm').addEventListener('submit', async (e) => {
e.preventDefault();
const userId = document.getElementById('userId').value;
const user = {
username: document.getElementById('username').value,
email: document.getElementById('email').value,
phone: document.getElementById('phone').value,
bio: document.getElementById('bio').value,
active: document.getElementById('active').value === 'true'
};
const resultDiv = document.getElementById('formResult');
resultDiv.className = 'result';
resultDiv.textContent = '保存中...';
try {
const url = userId ? `/api/users/${userId}` : '/api/users';
const method = userId ? 'PUT' : 'POST';
const res = await fetch(url, {
method: method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(user)
});
const data = await res.json();
if (res.ok) {
resultDiv.className = 'result success';
resultDiv.innerHTML = `<strong>✅ 保存成功!</strong><br>ID: ${data.id}, 用户名: ${data.username}`;
resetForm();
loadUsers();
} else {
resultDiv.className = 'result error';
resultDiv.innerHTML = `<strong>❌ 保存失败</strong><br>${JSON.stringify(data)}`;
}
} catch (e) {
resultDiv.className = 'result error';
resultDiv.innerHTML = `<strong>❌ 错误</strong><br>${e.message}`;
}
});
function editUser(id) {
const user = users.find(u => u.id === id);
if (!user) return;
document.getElementById('userId').value = user.id;
document.getElementById('username').value = user.username;
document.getElementById('email').value = user.email;
document.getElementById('phone').value = user.phone || '';
document.getElementById('bio').value = user.bio || '';
document.getElementById('active').value = user.active.toString();
document.getElementById('formResult').innerHTML = '<span style="color:#11998e;">✏️ 正在编辑用户 #' + id + '</span>';
window.scrollTo(0, 0);
}
async function deleteUser(id) {
if (!confirm(`确定要删除用户 #${id} 吗?`)) return;
try {
const res = await fetch(`/api/users/${id}`, { method: 'DELETE' });
if (res.ok) {
alert('✅ 删除成功');
loadUsers();
} else {
alert('❌ 删除失败');
}
} catch (e) {
alert('删除失败: ' + e.message);
}
}
function resetForm() {
document.getElementById('userForm').reset();
document.getElementById('userId').value = '';
document.getElementById('formResult').innerHTML = '';
}
// 初始化
loadUsers();
</script>
</body>
</html>