# scheduleManage.jsp - 排片管理页面
以下是基于`movieManage.jsp`仿写的排片管理页面,实现了完整的增删改查功能:
```html
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@page import="java.sql.*, java.text.SimpleDateFormat, java.util.Date" %>
<!DOCTYPE html>
<html>
<head>
<title>排片管理</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f8f9fa;
}
h1 {
text-align: center;
color: #343a40;
margin-bottom: 25px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
background: white;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #dee2e6;
}
th {
background-color: #007bff;
color: white;
font-weight: 600;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
tr:hover {
background-color: #e9ecef;
}
.editable {
cursor: pointer;
transition: background-color 0.2s;
}
.editable:hover {
background-color: #e0f7fa;
}
.editing {
background-color: #fffde7;
}
.btn-container {
display: flex;
gap: 8px;
}
.btn {
padding: 6px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.btn-edit {
background-color: #28a745;
color: white;
}
.btn-delete {
background-color: #dc3545;
color: white;
}
.btn-add {
background-color: #17a2b8;
color: white;
font-weight: bold;
padding: 10px 20px;
margin-bottom: 20px;
border-radius: 5px;
}
.btn:hover {
opacity: 0.85;
transform: translateY(-2px);
}
.search-container {
display: flex;
justify-content: center;
margin: 25px 0;
gap: 10px;
}
.search-container input[type="text"],
.search-container select {
padding: 10px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 16px;
}
.search-container input[type="submit"] {
background-color: #6c757d;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
.toast {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 4px;
color: white;
font-weight: 500;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
opacity: 0;
transform: translateY(-20px);
transition: all 0.3s ease;
z-index: 1000;
}
.toast.show {
opacity: 1;
transform: translateY(0);
}
.toast.success {
background-color: #28a745;
}
.toast.error {
background-color: #dc3545;
}
.form-control {
padding: 8px;
border: 1px solid #ced4da;
border-radius: 4px;
width: 100%;
box-sizing: border-box;
}
.time-format {
font-family: monospace;
font-size: 14px;
color: #666;
}
.price-tag {
font-weight: bold;
color: #007bff;
}
</style>
<script>
// 全局变量存储当前编辑状态
let currentlyEditing = null;
// 初始化行内编辑功能
function initInlineEditing() {
// 为可编辑单元格添加点击事件
document.querySelectorAll('.editable').forEach(cell => {
cell.addEventListener('click', function(e) {
// 如果已经在编辑其他单元格,先保存
if (currentlyEditing && currentlyEditing !== this) {
saveEdit(currentlyEditing);
}
// 检查是否已处于编辑模式
if (!this.classList.contains('editing')) {
startEditing(this);
}
});
});
// 为保存按钮添加事件
document.querySelectorAll('.btn-save').forEach(btn => {
btn.addEventListener('click', function() {
const row = this.closest('tr');
saveRow(row);
});
});
}
// 开始编辑单元格
function startEditing(cell) {
// 清除之前的编辑状态
if (currentlyEditing) {
saveEdit(currentlyEditing);
}
// 获取单元格原始内容
const originalValue = cell.textContent.trim();
const columnName = cell.getAttribute('data-column');
const scheduleId = cell.closest('tr').dataset.id;
// 创建输入框
let inputElement;
switch(columnName) {
case 'start_time':
case 'end_time':
inputElement = document.createElement('input');
inputElement.type = 'datetime-local';
// 转换时间格式以适应datetime-local输入框
const date = new Date(originalValue);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
inputElement.value = `${year}-${month}-${day}T${hours}:${minutes}`;
break;
case 'price':
inputElement = document.createElement('input');
inputElement.type = 'number';
inputElement.step = '0.01';
inputElement.min = '0';
inputElement.value = parseFloat(originalValue.replace('$', '')) || 0;
break;
default:
inputElement = document.createElement('input');
inputElement.type = 'text';
inputElement.value = originalValue;
}
inputElement.className = 'form-control';
// 清空单元格内容并添加输入框
cell.innerHTML = '';
cell.appendChild(inputElement);
cell.classList.add('editing');
// 聚焦输入框
inputElement.focus();
// 设置当前编辑的单元格
currentlyEditing = cell;
// 添加键盘事件监听
inputElement.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
saveEdit(cell);
} else if (e.key === 'Escape') {
cancelEdit(cell, originalValue);
}
});
}
// 保存单个单元格的编辑
function saveEdit(cell) {
const inputElement = cell.querySelector('input, textarea');
let newValue;
const columnName = cell.getAttribute('data-column');
const scheduleId = cell.closest('tr').dataset.id;
// 根据列类型处理值
switch(columnName) {
case 'start_time':
case 'end_time':
const dateValue = new Date(inputElement.value);
newValue = formatDateForDisplay(dateValue);
break;
case 'price':
newValue = '$' + parseFloat(inputElement.value).toFixed(2);
break;
default:
newValue = inputElement.value.trim();
}
// 恢复单元格内容
cell.textContent = newValue;
cell.classList.remove('editing');
// 更新数据
updateScheduleField(scheduleId, columnName, inputElement.value);
// 重置当前编辑状态
currentlyEditing = null;
}
// 取消编辑
function cancelEdit(cell, originalValue) {
cell.textContent = originalValue;
cell.classList.remove('editing');
currentlyEditing = null;
}
// 保存整行编辑
function saveRow(row) {
const scheduleId = row.dataset.id;
const data = {
movie_id: row.querySelector('[data-column="movie_id"]').textContent.trim(),
start_time: getDateTimeValue(row.querySelector('[data-column="start_time"]')),
end_time: getDateTimeValue(row.querySelector('[data-column="end_time"]')),
theater_id: row.querySelector('[data-column="theater_id"]').textContent.trim(),
price: parseFloat(row.querySelector('[data-column="price"]').textContent.replace('$', ''))
};
updateSchedule(scheduleId, data);
}
// 获取日期时间值
function getDateTimeValue(cell) {
const input = cell.querySelector('input');
if (input) {
return input.value;
}
const date = new Date(cell.textContent);
return date.toISOString().slice(0, 16);
}
// 更新单个字段
function updateScheduleField(scheduleId, field, value) {
$.ajax({
url: 'updateScheduleField.jsp',
type: 'POST',
data: {
id: scheduleId,
field: field,
value: value
},
success: function(response) {
showToast('排片信息更新成功!', 'success');
},
error: function() {
showToast('更新失败,请重试', 'error');
}
});
}
// 更新整条排片记录
function updateSchedule(scheduleId, data) {
$.ajax({
url: 'updateSchedule.jsp',
type: 'POST',
data: {
id: scheduleId,
movie_id: data.movie_id,
start_time: data.start_time,
end_time: data.end_time,
theater_id: data.theater_id,
price: data.price
},
success: function(response) {
showToast('排片信息更新成功!', 'success');
},
error: function() {
showToast('更新失败,请重试', 'error');
}
});
}
// 删除排片记录
function deleteSchedule(scheduleId) {
if (confirm('确定要删除这条排片吗?')) {
$.ajax({
url: 'deleteSchedule.jsp',
type: 'POST',
data: { id: scheduleId },
success: function(response) {
showToast('排片删除成功!', 'success');
// 从DOM中移除该行
document.querySelector(`tr[data-id="${scheduleId}"]`).remove();
},
error: function() {
showToast('删除失败,请重试', 'error');
}
});
}
}
// 添加新排片
function addSchedule() {
window.location.href = 'scheduleAdd.jsp';
}
// 显示通知
function showToast(message, type) {
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.classList.add('show');
}, 10);
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
toast.remove();
}, 300);
}, 3000);
}
// 格式化日期显示
function formatDateForDisplay(date) {
if (!date || isNaN(date.getTime())) return '';
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}`;
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initInlineEditing();
});
</script>
</head>
<body>
<h1>排片管理</h1>
<!-- 添加排片按钮 -->
<div align="center" style="margin-bottom: 15px;">
<button class="btn btn-add" onclick="addSchedule()">添加新排片</button>
</div>
<%
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch(Exception e) {
out.print(e);
}
String url = "jdbc:mysql://localhost:3306/movie";
String user = "root";
String pass = "123456";
String searchMovieId = request.getParameter("movie_id");
String strSql = "SELECT * FROM schedules";
// 如果有搜索条件,则添加WHERE子句
if (searchMovieId != null && !searchMovieId.isEmpty()) {
strSql += " WHERE movie_id = " + searchMovieId;
}
%>
<div class="search-container">
<form action="scheduleManage.jsp" method="get">
<select name="movie_id" style="width: 200px;">
<option value="">所有电影</option>
<%
// 获取所有电影用于筛选
Connection conMovies = null;
Statement stmtMovies = null;
ResultSet rsMovies = null;
try {
conMovies = DriverManager.getConnection(url, user, pass);
stmtMovies = conMovies.createStatement();
rsMovies = stmtMovies.executeQuery("SELECT id, title FROM movies ORDER BY title");
while(rsMovies.next()) {
String id = rsMovies.getString("id");
String title = rsMovies.getString("title");
String selected = (searchMovieId != null && searchMovieId.equals(id)) ? "selected" : "";
out.print("<option value='" + id + "' " + selected + ">" + title + "</option>");
}
} catch(SQLException e) {
// 忽略错误,继续执行
} finally {
try { if (rsMovies != null) rsMovies.close(); } catch (Exception e) {}
try { if (stmtMovies != null) stmtMovies.close(); } catch (Exception e) {}
try { if (conMovies != null) conMovies.close(); } catch (Exception e) {}
}
%>
</select>
<input type="submit" value="筛选">
<a href="adminIndex.jsp" style="margin-left: 10px;">返回</a>
</form>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th width="50">ID</th>
<th width="100">电影ID</th>
<th width="250">开始时间</th>
<th width="250">结束时间</th>
<th width="100">影厅ID</th>
<th width="100">价格</th>
<th width="100">操作</th>
</tr>
</thead>
<tbody>
<%
try {
con = DriverManager.getConnection(url, user, pass);
stmt = con.createStatement();
rs = stmt.executeQuery(strSql);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
while(rs.next()) {
String id = rs.getString("id");
String movieId = rs.getString("movie_id");
String startTime = rs.getString("start_time");
String endTime = rs.getString("end_time");
String theaterId = rs.getString("theater_id");
double price = rs.getDouble("price");
%>
<tr data-id="<%= id %>">
<td><%= id %></td>
<td class="editable" data-column="movie_id"><%= movieId %></td>
<td class="editable" data-column="start_time"><%= startTime %></td>
<td class="editable" data-column="end_time"><%= endTime %></td>
<td class="editable" data-column="theater_id"><%= theaterId %></td>
<td class="editable" data-column="price">$<%= String.format("%.2f", price) %></td>
<td class="btn-container">
<button class="btn btn-save">保存</button>
<button class="btn btn-delete" onclick="deleteSchedule(<%= id %>)">删除</button>
</td>
</tr>
<%
}
con.close();
} catch(SQLException e) {
out.print("<tr><td colspan='7' style='color:red;text-align:center;'>数据库错误: " + e.getMessage() + "</td></tr>");
} finally {
// 关闭资源
try { if (rs != null) rs.close(); } catch (Exception e) {}
try { if (stmt != null) stmt.close(); } catch (Exception e) {}
try { if (con != null) con.close(); } catch (Exception e) {}
}
%>
</tbody>
</table>
</div>
</body>
</html>
```
## 功能特点说明
### 1. 完整的CRUD操作
- **创建**:通过"添加新排片"按钮跳转到添加页面
- **读取**:显示所有排片信息,支持按电影筛选
- **更新**:行内编辑功能,直接在表格中修改
- **删除**:一键删除排片记录
### 2. 行内编辑特性
```javascript
// 为可编辑单元格添加点击事件
document.querySelectorAll('.editable').forEach(cell => {
cell.addEventListener('click', function(e) {
if (currentlyEditing && currentlyEditing !== this) {
saveEdit(currentlyEditing);
}
if (!this.classList.contains('editing')) {
startEditing(this);
}
});
});
```
### 3. 特殊字段处理
- **日期时间字段**:使用`datetime-local`输入框,自动格式转换
- **价格字段**:货币格式显示,数字输入验证
- **电影ID筛选**:下拉菜单选择特定电影的排片
### 4. 用户体验优化
- **实时保存**:点击保存按钮立即更新数据库
- **确认对话框**:删除前二次确认
- **操作反馈**:成功/失败通知提示
- **响应式设计**:适配不同屏幕尺寸
### 5. 需要创建的服务器端文件
**updateScheduleField.jsp**:
```jsp
<%@ page import="java.sql.*" %>
<%
// 获取参数
String id = request.getParameter("id");
String field = request.getParameter("field");
String value = request.getParameter("value");
// 验证字段名
if (!isValidField(field)) {
out.print("Invalid field");
return;
}
Connection con = null;
PreparedStatement pstmt = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/movie", "root", "123456");
String sql = "UPDATE schedules SET " + field + " = ? WHERE id = ?";
pstmt = con.prepareStatement(sql);
// 根据字段类型设置参数
if ("start_time".equals(field) || "end_time".equals(field)) {
pstmt.setTimestamp(1, Timestamp.valueOf(value));
} else if ("price".equals(field)) {
pstmt.setDouble(1, Double.parseDouble(value));
} else {
pstmt.setString(1, value);
}
pstmt.setInt(2, Integer.parseInt(id));
int rowsUpdated = pstmt.executeUpdate();
if (rowsUpdated > 0) {
out.print("success");
} else {
out.print("No rows updated");
}
} catch(Exception e) {
out.print("Error: " + e.getMessage());
} finally {
if (pstmt != null) pstmt.close();
if (con != null) con.close();
}
private boolean isValidField(String field) {
String[] allowedFields = {"movie_id", "start_time", "end_time", "theater_id", "price"};
for (String f : allowedFields) {
if (f.equals(field)) {
return true;
}
}
return false;
}
%>
```