本文目录导读:

- 项目结构
- 数据库结构 (database.sql)
- 配置文件 (config.php)
- 主页面 (index.php)
- 添加数据处理 (add_defense.php)
- 样式文件 (style.css)
- JavaScript (js/script.js)
- 使用方法
我可以帮你设计一个显示人盯人防守成功率的PHP项目,让我创建一个完整的示例:
项目结构
basketball-defense/
├── index.php # 主页面
├── config.php # 数据库配置
├── style.css # 样式文件
├── js/script.js # JavaScript文件
└── database.sql # 数据库结构
数据库结构 (database.sql)
CREATE DATABASE IF NOT EXISTS basketball_stats;
USE basketball_stats;
-- 球员表
CREATE TABLE players (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
position VARCHAR(20),
team VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 防守数据表
CREATE TABLE defense_stats (
id INT PRIMARY KEY AUTO_INCREMENT,
player_id INT,
match_date DATE,
opponent_player VARCHAR(50),
defensive_attempts INT DEFAULT 0, -- 防守次数
successful_defenses INT DEFAULT 0, -- 成功防守次数
blocks INT DEFAULT 0, -- 盖帽数
steals INT DEFAULT 0, -- 抢断数
fouls INT DEFAULT 0, -- 犯规数
FOREIGN KEY (player_id) REFERENCES players(id)
);
-- 示例数据
INSERT INTO players (name, position, team) VALUES
('张三', '后卫', '雷霆队'),
('李四', '前锋', '雷霆队'),
('王五', '中锋', '雷霆队'),
('赵六', '后卫', '勇士队');
INSERT INTO defense_stats (player_id, match_date, opponent_player, defensive_attempts, successful_defenses, blocks, steals, fouls) VALUES
(1, '2024-01-15', '库里', 12, 8, 1, 2, 3),
(1, '2024-01-20', '詹姆斯', 15, 10, 2, 1, 2),
(2, '2024-01-15', '杜兰特', 10, 6, 1, 1, 4),
(2, '2024-01-20', '字母哥', 14, 9, 3, 0, 2),
(3, '2024-01-15', '约基奇', 18, 12, 4, 1, 3),
(3, '2024-01-20', '恩比德', 16, 11, 3, 0, 2);
配置文件 (config.php)
<?php
session_start();
// 数据库配置
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'basketball_stats');
// 连接数据库
function dbConnect() {
try {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
throw new Exception("连接失败: " . $conn->connect_error);
}
$conn->set_charset("utf8mb4");
return $conn;
} catch (Exception $e) {
die("数据库连接错误: " . $e->getMessage());
}
}
// 计算防守成功率
function calculateDefenseRate($successful, $attempts) {
if ($attempts <= 0) return 0;
return round(($successful / $attempts) * 100, 2);
}
// 获取球员防守统计数据
function getPlayerDefenseStats($playerId = null) {
$conn = dbConnect();
$where = $playerId ? "WHERE p.id = $playerId" : "";
$sql = "SELECT
p.id,
p.name,
p.position,
p.team,
COUNT(ds.id) as matches_played,
SUM(ds.defensive_attempts) as total_attempts,
SUM(ds.successful_defenses) as total_successful,
SUM(ds.blocks) as total_blocks,
SUM(ds.steals) as total_steals,
SUM(ds.fouls) as total_fouls,
AVG(ds.defensive_attempts) as avg_attempts_per_match,
AVG(ds.successful_defenses) as avg_successful_per_match
FROM players p
LEFT JOIN defense_stats ds ON p.id = ds.player_id
$where
GROUP BY p.id
ORDER BY (SUM(ds.successful_defenses)/NULLIF(SUM(ds.defensive_attempts),0)) DESC";
$result = $conn->query($sql);
$stats = [];
if ($result) {
while ($row = $result->fetch_assoc()) {
// 计算防守成功率
$row['defense_rate'] = calculateDefenseRate(
$row['total_successful'],
$row['total_attempts']
);
// 计算场均防守效率评分
$row['defense_rating'] = calculateDefenseRating($row);
$stats[] = $row;
}
}
$conn->close();
return $stats;
}
// 计算防守效率评分 (综合评分)
function calculateDefenseRating($stats) {
$rate = $stats['defense_rate'];
$blocks_effect = ($stats['total_blocks'] ?? 0) * 2;
$steals_effect = ($stats['total_steals'] ?? 0) * 1.5;
$fouls_penalty = ($stats['total_fouls'] ?? 0) * -0.5;
return round($rate + $blocks_effect + $steals_effect + $fouls_penalty, 2);
}
?>
主页面 (index.php)
<?php
require_once 'config.php';
// 获取筛选参数
$selectedPlayer = isset($_GET['player_id']) ? $_GET['player_id'] : null;
$dateFrom = isset($_GET['date_from']) ? $_GET['date_from'] : '';
$dateTo = isset($_GET['date_to']) ? $_GET['date_to'] : '';
// 获取统计数据
$stats = getPlayerDefenseStats($selectedPlayer);
// 获取所有球员用于筛选
$conn = dbConnect();
$players = $conn->query("SELECT id, name FROM players ORDER BY name");
$conn->close();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">人盯人防守成功率分析系统</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<header>
<h1>🏀 人盯人防守成功率分析</h1>
<p class="subtitle">实时监控球队防守表现</p>
</header>
<nav class="filter-section">
<form method="GET" action="index.php" class="filter-form">
<div class="filter-group">
<label>球员筛选:</label>
<select name="player_id">
<option value="">全部球员</option>
<?php while($player = $players->fetch_assoc()): ?>
<option value="<?php echo $player['id']; ?>"
<?php echo $selectedPlayer == $player['id'] ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($player['name']); ?>
</option>
<?php endwhile; ?>
</select>
</div>
<div class="filter-group">
<label>开始日期:</label>
<input type="date" name="date_from" value="<?php echo $dateFrom; ?>">
</div>
<div class="filter-group">
<label>结束日期:</label>
<input type="date" name="date_to" value="<?php echo $dateTo; ?>">
</div>
<button type="submit" class="btn-filter">筛选数据</button>
<a href="index.php" class="btn-reset">重置</a>
</form>
</nav>
<!-- 数据总览卡片 -->
<div class="summary-cards">
<?php
$avgRate = count($stats) > 0 ? array_sum(array_column($stats, 'defense_rate')) / count($stats) : 0;
$totalBlocks = array_sum(array_column($stats, 'total_blocks'));
$totalSteals = array_sum(array_column($stats, 'total_steals'));
?>
<div class="card">
<div class="card-icon">📊</div>
<div class="card-value"><?php echo round($avgRate, 1); ?>%</div>
<div class="card-label">平均防守成功率</div>
</div>
<div class="card">
<div class="card-icon">🛡️</div>
<div class="card-value"><?php echo $totalBlocks; ?></div>
<div class="card-label">总盖帽数</div>
</div>
<div class="card">
<div class="card-icon">✋</div>
<div class="card-value"><?php echo $totalSteals; ?></div>
<div class="card-label">总抢断数</div>
</div>
<div class="card">
<div class="card-icon">🎯</div>
<div class="card-value"><?php echo count($stats); ?></div>
<div class="card-label">球员数量</div>
</div>
</div>
<!-- 主要数据表格 -->
<div class="table-container">
<h2>球员防守统计排名</h2>
<table>
<thead>
<tr>
<th>排名</th>
<th>球员</th>
<th>位置</th>
<th>球队</th>
<th>场次</th>
<th>防守次数</th>
<th>成功防守</th>
<th>成功率</th>
<th>盖帽</th>
<th>抢断</th>
<th>犯规</th>
<th>防守评分</th>
<th>表现</th>
</tr>
</thead>
<tbody>
<?php $rank = 1; foreach($stats as $stat): ?>
<tr>
<td>
<span class="rank <?php echo $rank <= 3 ? 'top-3' : ''; ?>">
<?php echo $rank; ?>
</span>
</td>
<td><?php echo htmlspecialchars($stat['name']); ?></td>
<td><?php echo htmlspecialchars($stat['position']); ?></td>
<td><?php echo htmlspecialchars($stat['team']); ?></td>
<td><?php echo $stat['matches_played']; ?></td>
<td><?php echo $stat['total_attempts']; ?></td>
<td><?php echo $stat['total_successful']; ?></td>
<td class="rate-cell">
<div class="progress-wrapper">
<div class="progress-bar" style="width: <?php echo $stat['defense_rate']; ?>%;"></div>
<span><?php echo $stat['defense_rate']; ?>%</span>
</div>
</td>
<td><?php echo $stat['total_blocks']; ?></td>
<td><?php echo $stat['total_steals']; ?></td>
<td><?php echo $stat['total_fouls']; ?></td>
<td class="rating-cell">
<?php echo $stat['defense_rating']; ?>
<?php
$ratingClass = $stat['defense_rating'] > 70 ? 'excellent' : ($stat['defense_rating'] > 50 ? 'good' : 'needs-improvement');
?>
<div class="rating-dot <?php echo $ratingClass; ?>"></div>
</td>
<td>
<?php
$performance = $stat['defense_rating'] > 70 ? '🎖️' : ($stat['defense_rating'] > 50 ? '💪' : '📝');
echo $performance;
?>
</td>
</tr>
<?php $rank++; endforeach; ?>
</tbody>
</table>
</div>
<!-- 添加新数据的表单 -->
<div class="data-entry-section">
<h2>添加防守数据</h2>
<form method="POST" action="add_defense.php" class="data-form">
<div class="form-grid">
<div class="form-group">
<label>球员</label>
<select name="player_id" required>
<option value="">选择球员...</option>
<?php
$conn = dbConnect();
$playersList = $conn->query("SELECT id, name FROM players ORDER BY name");
while($p = $playersList->fetch_assoc()):
?>
<option value="<?php echo $p['id']; ?>"><?php echo $p['name']; ?></option>
<?php endwhile; ?>
</select>
</div>
<div class="form-group">
<label>对手球员</label>
<input type="text" name="opponent_player" required>
</div>
<div class="form-group">
<label>比赛日期</label>
<input type="date" name="match_date" required>
</div>
<div class="form-group">
<label>防守次数</label>
<input type="number" name="defensive_attempts" min="0" required>
</div>
<div class="form-group">
<label>成功防守次数</label>
<input type="number" name="successful_defenses" min="0" required>
</div>
<div class="form-group">
<label>盖帽数</label>
<input type="number" name="blocks" min="0" value="0">
</div>
<div class="form-group">
<label>抢断数</label>
<input type="number" name="steals" min="0" value="0">
</div>
<div class="form-group">
<label>犯规数</label>
<input type="number" name="fouls" min="0" value="0">
</div>
</div>
<button type="submit" class="btn-submit">提交数据</button>
</form>
</div>
</div>
<script src="js/script.js"></script>
</body>
</html>
添加数据处理 (add_defense.php)
<?php
require_once 'config.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$player_id = $_POST['player_id'];
$opponent_player = $_POST['opponent_player'];
$match_date = $_POST['match_date'];
$defensive_attempts = $_POST['defensive_attempts'];
$successful_defenses = $_POST['successful_defenses'];
$blocks = $_POST['blocks'];
$steals = $_POST['steals'];
$fouls = $_POST['fouls'];
// 验证数据
if ($successful_defenses > $defensive_attempts) {
header('Location: index.php?error=successful_defenses_exceed');
exit;
}
$conn = dbConnect();
$sql = "INSERT INTO defense_stats
(player_id, opponent_player, match_date, defensive_attempts, successful_defenses, blocks, steals, fouls)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("issiiiii", $player_id, $opponent_player, $match_date, $defensive_attempts, $successful_defenses, $blocks, $steals, $fouls);
if ($stmt->execute()) {
header('Location: index.php?success=1');
} else {
header('Location: index.php?error=db_error');
}
$stmt->close();
$conn->close();
}
?>
样式文件 (style.css)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: #fff;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
header h1 {
font-size: 2.5em;
margin-bottom: 10px;
}
{
opacity: 0.9;
font-size: 1.2em;
}
/* 筛选区域 */
.filter-section {
background: #f8f9fa;
padding: 20px;
border-bottom: 1px solid #dee2e6;
}
.filter-form {
display: flex;
flex-wrap: wrap;
gap: 15px;
align-items: flex-end;
}
.filter-group {
display: flex;
flex-direction: column;
gap: 5px;
}
.filter-group label {
font-weight: bold;
color: #495057;
font-size: 14px;
}
.filter-group select,
.filter-group input {
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 5px;
font-size: 14px;
}
/* 统计卡片 */
.summary-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
padding: 30px;
background: #f8f9fa;
}
.card {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
text-align: center;
transition: transform 0.3s;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0,0,0,0.15);
}
.card-icon {
font-size: 2.5em;
margin-bottom: 10px;
}
.card-value {
font-size: 2em;
font-weight: bold;
color: #667eea;
}
.card-label {
color: #6c757d;
font-size: 14px;
margin-top: 5px;
}
/* 表格样式 */
.table-container {
padding: 30px;
}
.table-container h2 {
margin-bottom: 20px;
color: #343a40;
}
table {
width: 100%;
border-collapse: collapse;
background: white;
}
th {
background: #667eea;
color: white;
padding: 12px;
text-align: left;
font-size: 14px;
white-space: nowrap;
}
td {
padding: 10px;
border-bottom: 1px solid #dee2e6;
font-size: 14px;
}
tr:hover {
background-color: #f8f9fa;
}
.rank {
display: inline-block;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
border-radius: 50%;
background: #e9ecef;
color: #343a40;
font-weight: bold;
}
.rank.top-3 {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
color: white;
}
/* 进度条 */
.progress-wrapper {
width: 150px;
height: 20px;
background: #e9ecef;
border-radius: 10px;
position: relative;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
border-radius: 10px;
transition: width 0.3s;
}
.progress-wrapper span {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 12px;
font-weight: bold;
color: white;
text-shadow: 1px 1px 2px rgba(0,0,0,0.3);
}
/* 评分 */
.rating-cell {
position: relative;
}
.rating-dot {
position: absolute;
top: 5px;
right: 5px;
width: 10px;
height: 10px;
border-radius: 50%;
}
.rating-dot.excellent {
background: #28a745;
animation: pulse 1s infinite;
}
.rating-dot.good {
background: #ffc107;
}
.rating-dot.needs-improvement {
background: #dc3545;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.3); }
100% { transform: scale(1); }
}
/* 添加数据表单 */
.data-entry-section {
padding: 30px;
background: #f8f9fa;
border-top: 1px solid #dee2e6;
}
.data-entry-section h2 {
margin-bottom: 20px;
color: #343a40;
}
.data-form {
background: white;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.form-group {
display: flex;
flex-direction: column;
gap: 5px;
}
.form-group label {
font-weight: bold;
color: #495057;
}
.form-group input,
.form-group select {
padding: 8px 12px;
border: 1px solid #ced4da;
border-radius: 5px;
}
.btn-submit,
.btn-filter {
background: #667eea;
color: white;
padding: 12px 24px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
transition: background 0.3s;
}
.btn-submit:hover,
.btn-filter:hover {
background: #5a67d8;
}
.btn-reset {
background: #6c757d;
color: white;
padding: 12px 24px;
border: none;
border-radius: 5px;
text-decoration: none;
}
/* 响应式设计 */
@media (max-width: 768px) {
.filter-form {
flex-direction: column;
}
.summary-cards {
grid-template-columns: repeat(2, 1fr);
padding: 15px;
}
.table-container {
padding: 15px;
overflow-x: auto;
}
.data-entry-section {
padding: 15px;
}
.form-grid {
grid-template-columns: 1fr;
}
}
JavaScript (js/script.js)
// 数据过滤和交互逻辑
document.addEventListener('DOMContentLoaded', function() {
// 添加表单验证
const dataForm = document.querySelector('.data-form');
if (dataForm) {
dataForm.addEventListener('submit', function(e) {
const attempts = parseInt(document.querySelector('input[name="defensive_attempts"]').value);
const successful = parseInt(document.querySelector('input[name="successful_defenses"]').value);
if (successful > attempts) {
e.preventDefault();
alert('错误:成功防守次数不能大于防守总次数!');
}
});
}
// 显示提示信息
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('success')) {
showNotification('数据添加成功!', 'success');
}
if (urlParams.get('error')) {
const errors = {
'successful_defenses_exceed': '成功防守次数不能大于总防守次数',
'db_error': '数据库错误,请稍后重试'
};
const errorMsg = errors[urlParams.get('error')] || '发生错误';
showNotification(errorMsg, 'error');
}
// 排名动画
animateRanks();
// 进度条动画
animateProgressBars();
});
function showNotification(message, type) {
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.innerHTML = `
<span>${message}</span>
<button onclick="this.parentElement.remove()">×</button>
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.remove();
}, 3000);
}
function animateRanks() {
const ranks = document.querySelectorAll('.rank');
ranks.forEach((rank, index) => {
setTimeout(() => {
rank.classList.add('animated');
}, index * 100);
});
}
function animateProgressBars() {
const bars = document.querySelectorAll('.progress-bar');
bars.forEach(bar => {
const targetWidth = bar.style.width;
bar.style.width = '0%';
setTimeout(() => {
bar.style.width = targetWidth;
}, 300);
});
}
// 添加CSS动画样式
const style = document.createElement('style');
style.textContent = `
.rank.animated {
animation: spinIn 0.5s ease-out;
}
@keyframes spinIn {
from { transform: rotate(0deg) scale(0); }
to { transform: rotate(360deg) scale(1); }
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 8px;
color: white;
font-weight: bold;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
z-index: 1000;
animation: slideIn 0.3s ease-out;
}
.notification.success {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
}
.notification.error {
background: linear-gradient(135deg, #dc3545 0%, #f5576c 100%);
}
@keyframes slideIn {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
`;
document.head.appendChild(style);
使用方法
-
环境要求:PHP 7.0+、MySQL 5.7+
-
安装步骤:
# 导入数据库 mysql -u root -p < database.sql # 将文件部署到PHP服务器 # 访问 http://localhost/basketball-defense/index.php
-
功能特点:
- 📊 实时计算防守成功率
- 🏆 球员防守排名
- 📈 可视化数据展示
- 🎯 综合防守评分
- 📝 数据录入和管理
- 🔍 按球员和日期筛选
这个项目提供了完整的人盯人防守成功率分析系统,包含数据录入、统计计算、可视化展示等功能,你可以根据实际需求进行修改和扩展。