上传完整架构

This commit is contained in:
2026-04-14 12:29:29 +08:00
parent fc8f9b61cc
commit 8c15cdf41d
93 changed files with 6849 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Class-Path:

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>HRS</display-name>
<!-- 欢迎页面 -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.hrs.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

5
HRS/WebContent/index.jsp Normal file
View File

@@ -0,0 +1,5 @@
<%@ page contentType="text/html; charset=UTF-8" language="java" %>
<%
// 重定向到登录页面
response.sendRedirect(request.getContextPath() + "/user/login");
%>

View File

@@ -0,0 +1,502 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"2".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<House> pendingList = (List<House>) request.getAttribute("pendingList");
List<House> publishedList = (List<House>) request.getAttribute("publishedList");
List<House> offlineList = (List<House>) request.getAttribute("offlineList");
Integer pendingCount = (Integer) request.getAttribute("pendingCount");
Integer publishedCount = (Integer) request.getAttribute("publishedCount");
Integer offlineCount = (Integer) request.getAttribute("offlineCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>房源审核 - 管理员后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #2196F3;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #1976D2;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stats-row {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background-color: white;
padding: 15px 25px;
border-radius: 8px;
flex: 1;
text-align: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.stat-number {
font-size: 32px;
font-weight: bold;
margin-bottom: 5px;
}
.stat-label {
color: #666;
font-size: 14px;
}
.stat-pending .stat-number { color: #f39c12; }
.stat-published .stat-number { color: #4CAF50; }
.stat-offline .stat-number { color: #f44336; }
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.house-section {
background-color: white;
border-radius: 8px;
margin-bottom: 30px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.section-title {
background-color: #f5f5f5;
padding: 15px 20px;
font-size: 18px;
font-weight: bold;
border-bottom: 2px solid #2196F3;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #fafafa;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-published {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-offline {
background-color: #ffebee;
color: #f44336;
}
.btn {
padding: 4px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
margin: 0 3px;
}
.btn-approve {
background-color: #4CAF50;
color: white;
}
.btn-reject {
background-color: #f44336;
color: white;
}
.btn-offline {
background-color: #ff9800;
color: white;
}
.btn-online {
background-color: #2196F3;
color: white;
}
.btn-view {
background-color: #999;
color: white;
}
.btn-approve:hover { background-color: #45a049; }
.btn-reject:hover { background-color: #d32f2f; }
.btn-offline:hover { background-color: #fb8c00; }
.btn-online:hover { background-color: #1976D2; }
.btn-view:hover { background-color: #777; }
.empty-state {
text-align: center;
padding: 40px;
color: #999;
}
.price {
color: #f44336;
font-weight: bold;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">👑 管理员后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/jsp/admin/index.jsp" >首页</a>
<a href="${pageContext.request.contextPath}/admin/user/list">用户管理</a>
<a href="${pageContext.request.contextPath}/admin/house/list" class="active">房源审核</a>
<a href="${pageContext.request.contextPath}/admin/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/admin/house/list">首页</a> &gt;
<span>房源审核</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<!-- 统计卡片 -->
<div class="stats-row">
<div class="stat-card stat-pending">
<div class="stat-number"><%= pendingCount %></div>
<div class="stat-label">待审核房源</div>
</div>
<div class="stat-card stat-published">
<div class="stat-number"><%= publishedCount %></div>
<div class="stat-label">已上架房源</div>
</div>
<div class="stat-card stat-offline">
<div class="stat-number"><%= offlineCount %></div>
<div class="stat-label">已下架/违规房源</div>
</div>
</div>
<!-- 待审核房源 -->
<div class="house-section">
<div class="section-title">
⏳ 待审核房源
<span style="font-size:14px; color:#999; margin-left:10px;">共 <%= pendingCount %> 条</span>
</div>
<table>
<thead>
<tr>
<th>房源图片</th>
<th>房源信息</th>
<th>房东信息</th>
<th>租金</th>
<th>发布时间</th>
<th>操作</th>
</thead>
<tbody>
<%
if (pendingList != null && !pendingList.isEmpty()) {
for (House house : pendingList) {
%>
<tr>
<td style="width:80px;">
<div style="width:70px; height:50px; background-color:#e0e0e0; background-size:cover; background-position:center; border-radius:4px;
<% if (house.getFirstImage() != null) { %>background-image:url('/HRS/<%= house.getFirstImage() %>');<% } %>">
<% if (house.getFirstImage() == null) { %>
<div style="display:flex;align-items:center;justify-content:center;height:100%;font-size:12px;">无图</div>
<% } %>
</div>
</td>
<td>
<strong><%= house.getTitle() %></strong><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %> | <%= house.getHouseType() %></span>
</td>
<td>
<%= house.getLandlordName() != null ? house.getLandlordName() : "未知" %><br>
<span style="font-size:12px;color:#999;"><%= house.getLandlordPhone() != null ? house.getLandlordPhone() : "" %></span>
</td>
<td class="price">¥<%= house.getRentPrice() %>/月</td>
<td><%= sdf.format(house.getCreateTime()) %></td>
<td>
<a href="${pageContext.request.contextPath}/admin/house/audit?id=<%= house.getId() %>&action=approve"
class="btn btn-approve" onclick="return confirm('确定通过审核吗?房源将立即上架。')">通过</a>
<a href="${pageContext.request.contextPath}/admin/house/audit?id=<%= house.getId() %>&action=reject"
class="btn btn-reject" onclick="return confirm('确定拒绝该房源吗?')">拒绝</a>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>"
class="btn btn-view" target="_blank">预览</a>
</tr>
<%
}
} else {
%>
<tr><td colspan="6" class="empty-state">暂无待审核房源</td></tr>
<%
}
%>
</tbody>
</table>
</div>
<!-- 已上架房源 -->
<div class="house-section">
<div class="section-title">
✅ 已上架房源
<span style="font-size:14px; color:#999; margin-left:10px;">共 <%= publishedCount %> 条</span>
</div>
<table>
<thead>
<tr>
<th>房源图片</th>
<th>房源信息</th>
<th>房东信息</th>
<th>租金</th>
<th>上架时间</th>
<th>操作</th>
</thead>
<tbody>
<%
if (publishedList != null && !publishedList.isEmpty()) {
for (House house : publishedList) {
%>
<tr>
<td style="width:80px;">
<div style="width:70px; height:50px; background-color:#e0e0e0; background-size:cover; background-position:center; border-radius:4px;
<% if (house.getFirstImage() != null) { %>background-image:url('/HRS/<%= house.getFirstImage() %>');<% } %>">
<% if (house.getFirstImage() == null) { %>
<div style="display:flex;align-items:center;justify-content:center;height:100%;font-size:12px;">无图</div>
<% } %>
</div>
</td>
<td>
<strong><%= house.getTitle() %></strong><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %> | <%= house.getHouseType() %></span>
</td>
<td>
<%= house.getLandlordName() != null ? house.getLandlordName() : "未知" %>
</td>
<td class="price">¥<%= house.getRentPrice() %>/月</td>
<td><%= sdf.format(house.getCreateTime()) %></td>
<td>
<a href="${pageContext.request.contextPath}/admin/house/offline?id=<%= house.getId() %>"
class="btn btn-offline" onclick="return confirm('确定要下架该房源吗?')">下架</a>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>"
class="btn btn-view" target="_blank">预览</a>
</td>
</tr>
<%
}
} else {
%>
<tr><td colspan="6" class="empty-state">暂无已上架房源</td></tr>
<%
}
%>
</tbody>
</table>
</div>
<!-- 已下架/违规房源 -->
<div class="house-section">
<div class="section-title">
📥 已下架/违规房源
<span style="font-size:14px; color:#999; margin-left:10px;">共 <%= offlineCount %> 条</span>
</div>
<table>
<thead>
<tr>
<th>房源图片</th>
<th>房源信息</th>
<th>房东信息</th>
<th>租金</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
if (offlineList != null && !offlineList.isEmpty()) {
for (House house : offlineList) {
%>
<tr>
<td style="width:80px;">
<div style="width:70px; height:50px; background-color:#e0e0e0; background-size:cover; background-position:center; border-radius:4px;
<% if (house.getFirstImage() != null) { %>background-image:url('/HRS/<%= house.getFirstImage() %>');<% } %>">
<% if (house.getFirstImage() == null) { %>
<div style="display:flex;align-items:center;justify-content:center;height:100%;font-size:12px;">无图</div>
<% } %>
</div>
</td>
<td>
<strong><%= house.getTitle() %></strong><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %> | <%= house.getHouseType() %></span>
</td>
<td>
<%= house.getLandlordName() != null ? house.getLandlordName() : "未知" %>
</td>
<td class="price">¥<%= house.getRentPrice() %>/月</td>
<td>
<span class="status-badge status-offline"><%= house.getStatusText() %></span>
</td>
<td>
<a href="${pageContext.request.contextPath}/admin/house/online?id=<%= house.getId() %>"
class="btn btn-online" onclick="return confirm('确定要重新上架该房源吗?')">重新上架</a>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>"
class="btn btn-view" target="_blank">预览</a>
</td>
</tr>
<%
}
} else {
%>
<tr><td colspan="6" class="empty-state">暂无已下架房源</td></tr>
<%
}
%>
</tbody>
</table>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,303 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"2".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>管理员后台 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #2196F3;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.logo span {
font-size: 14px;
margin-left: 5px;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #1976D2;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #2196F3;
text-decoration: none;
}
.welcome-card {
background: linear-gradient(135deg, #2196F3 0%, #1976D2 100%);
color: white;
padding: 40px;
border-radius: 8px;
margin-bottom: 30px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
}
.welcome-card h1 {
font-size: 28px;
margin-bottom: 15px;
}
.welcome-card p {
font-size: 16px;
opacity: 0.9;
}
.stats-row {
display: flex;
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background-color: white;
padding: 25px;
border-radius: 8px;
flex: 1;
text-align: center;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
}
.stat-icon {
font-size: 48px;
margin-bottom: 10px;
}
.stat-number {
font-size: 32px;
font-weight: bold;
margin-bottom: 5px;
}
.stat-label {
color: #666;
font-size: 14px;
}
.stat-user .stat-number { color: #2196F3; }
.stat-house .stat-number { color: #4CAF50; }
.stat-order .stat-number { color: #ff9800; }
.stat-pending .stat-number { color: #f44336; }
.quick-actions {
background-color: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.quick-actions h3 {
margin-bottom: 20px;
color: #333;
border-left: 4px solid #2196F3;
padding-left: 15px;
}
.action-buttons {
display: flex;
gap: 15px;
flex-wrap: wrap;
}
.action-btn {
background-color: #f5f5f5;
padding: 15px 25px;
border-radius: 8px;
text-decoration: none;
color: #333;
transition: all 0.3s;
text-align: center;
min-width: 120px;
}
.action-btn:hover {
background-color: #2196F3;
color: white;
transform: translateY(-3px);
}
.action-btn .icon {
font-size: 24px;
display: block;
margin-bottom: 8px;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">👑 租房系统管理后台 <span>Administrator</span></div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/admin/index.jsp" class="active">首页</a>
<a href="${pageContext.request.contextPath}/admin/user/list">用户管理</a>
<a href="${pageContext.request.contextPath}/admin/house/list">房源审核</a>
<a href="${pageContext.request.contextPath}/admin/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/admin/index.jsp">首页</a> &gt;
<span>控制台</span>
</div>
<div class="welcome-card">
<h1>欢迎回来,<%= loginUser.getRealName() %></h1>
<p>今天是 <%= new java.text.SimpleDateFormat("yyyy年MM月dd日 EEEE").format(new java.util.Date()) %></p>
<p style="margin-top: 10px;">您可以通过下方面板管理系统数据</p>
</div>
<div class="stats-row" id="statsRow">
<div class="stat-card stat-user">
<div class="stat-icon">👥</div>
<div class="stat-number" id="userCount">-</div>
<div class="stat-label">注册用户</div>
</div>
<div class="stat-card stat-house">
<div class="stat-icon">🏠</div>
<div class="stat-number" id="houseCount">-</div>
<div class="stat-label">房源总数</div>
</div>
<div class="stat-card stat-order">
<div class="stat-icon">📦</div>
<div class="stat-number" id="orderCount">-</div>
<div class="stat-label">订单总数</div>
</div>
<div class="stat-card stat-pending">
<div class="stat-icon">⏳</div>
<div class="stat-number" id="pendingCount">-</div>
<div class="stat-label">待审核房源</div>
</div>
</div>
<div class="quick-actions">
<h3>快捷操作</h3>
<div class="action-buttons">
<a href="${pageContext.request.contextPath}/admin/user/list" class="action-btn">
<span class="icon">👤</span>
用户管理
</a>
<a href="${pageContext.request.contextPath}/admin/house/list" class="action-btn">
<span class="icon">🏠</span>
房源审核
</a>
<a href="${pageContext.request.contextPath}/admin/order/list" class="action-btn">
<span class="icon">📦</span>
订单管理
</a>
</div>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 管理员后台 | 让租房更简单</p>
</div>
<script>
// 加载统计数据
function loadStats() {
fetch('${pageContext.request.contextPath}/admin/statistics')
.then(response => response.json())
.then(data => {
document.getElementById('userCount').innerText = data.userCount || 0;
document.getElementById('houseCount').innerText = data.houseCount || 0;
document.getElementById('orderCount').innerText = data.orderCount || 0;
document.getElementById('pendingCount').innerText = data.pendingCount || 0;
})
.catch(err => {
console.log('加载统计数据失败,使用默认值');
document.getElementById('userCount').innerText = '-';
document.getElementById('houseCount').innerText = '-';
document.getElementById('orderCount').innerText = '-';
document.getElementById('pendingCount').innerText = '-';
});
}
loadStats();
</script>
</body>
</html>

View File

@@ -0,0 +1,280 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Order, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"2".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Order> orderList = (List<Order>) request.getAttribute("orderList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>订单管理 - 管理员后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #2196F3;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1300px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #1976D2;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #2196F3;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.order-table {
background-color: white;
border-radius: 8px;
overflow-x: auto;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
min-width: 1000px;
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #eee;
font-size: 14px;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-paid {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.price {
color: #f44336;
font-weight: bold;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">👑 管理员后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/jsp/admin/index.jsp" >首页</a>
<a href="${pageContext.request.contextPath}/admin/user/list">用户管理</a>
<a href="${pageContext.request.contextPath}/admin/house/list">房源审核</a>
<a href="${pageContext.request.contextPath}/admin/order/list" class="active">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/admin/order/list">首页</a> &gt;
<span>订单管理</span>
</div>
<div class="stats">
📋 共有 <strong><%= totalCount %></strong> 个订单
</div>
<div class="order-table">
<%
if (orderList != null && !orderList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>订单编号</th>
<th>租客信息</th>
<th>房东信息</th>
<th>房源信息</th>
<th>租赁时间</th>
<th>总租金</th>
<th>支付状态</th>
<th>订单状态</th>
<th>下单时间</th>
</tr>
</thead>
<tbody>
<%
for (Order order : orderList) {
House house = order.getHouse();
User tenant = order.getTenant();
User landlord = order.getLandlord();
%>
<tr>
<td><%= order.getOrderNo() %></td>
<td>
<%= tenant != null ? (tenant.getRealName() != null ? tenant.getRealName() : tenant.getUserName()) : "未知" %><br>
<span style="font-size:12px;color:#999;"><%= tenant != null ? tenant.getPhone() : "" %></span>
</td>
<td>
<%= landlord != null ? (landlord.getRealName() != null ? landlord.getRealName() : landlord.getUserName()) : "未知" %>
</td>
<td>
<strong><%= house != null ? house.getTitle() : "未知" %></strong><br>
<span style="font-size:12px;color:#999;"><%= house != null ? house.getArea() : "" %></span>
</td>
<td>
<%= sdf.format(order.getRentStartTime()) %> ~ <%= sdf.format(order.getRentEndTime()) %><br>
<span style="font-size:12px;color:#999;">共<%= order.getRentDays() %>天</span>
</td>
<td class="price">¥<%= order.getTotalRent() %></td>
<td>
<span class="status-badge status-<%= "0".equals(order.getPayStatus()) ? "pending" : "paid" %>">
<%= order.getPayStatusText() %>
</span>
</td>
<td>
<span class="status-badge status-pending">
<%= order.getStatusText() %>
</span>
</td>
<td><%= sdf.format(order.getCreateTime()) %></td>
</tr>
<%
}
%>
</tbody>
20
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📦</div>
<p>暂无订单</p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,453 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"2".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<User> tenants = (List<User>) request.getAttribute("tenants");
List<User> landlords = (List<User>) request.getAttribute("landlords");
List<User> admins = (List<User>) request.getAttribute("admins");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户管理 - 管理员后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #2196F3;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #1976D2;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.user-section {
background-color: white;
border-radius: 8px;
margin-bottom: 30px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.section-title {
background-color: #f5f5f5;
padding: 15px 20px;
font-size: 18px;
font-weight: bold;
border-bottom: 2px solid #2196F3;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #fafafa;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-active {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-disabled {
background-color: #ffebee;
color: #f44336;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.btn {
padding: 4px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
margin: 0 3px;
}
.btn-enable {
background-color: #4CAF50;
color: white;
}
.btn-disable {
background-color: #f44336;
color: white;
}
.btn-delete {
background-color: #999;
color: white;
}
.btn-enable:hover {
background-color: #45a049;
}
.btn-disable:hover {
background-color: #d32f2f;
}
.btn-delete:hover {
background-color: #777;
}
.empty-state {
text-align: center;
padding: 40px;
color: #999;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
.role-icon {
font-size: 18px;
margin-right: 5px;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">👑 管理员后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/jsp/admin/index.jsp" >首页</a>
<a href="${pageContext.request.contextPath}/admin/user/list" class="active">用户管理</a>
<a href="${pageContext.request.contextPath}/admin/house/list">房源审核</a>
<a href="${pageContext.request.contextPath}/admin/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/admin/user/list">首页</a> &gt;
<span>用户管理</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📊 共有 <strong><%= totalCount %></strong> 个注册用户
</div>
<!-- 租客列表 -->
<div class="user-section">
<div class="section-title">
<span class="role-icon">👤</span> 租客列表
<span style="font-size:14px; color:#999; margin-left:10px;">共 <%= tenants != null ? tenants.size() : 0 %> 人</span>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>真实姓名</th>
<th>手机号</th>
<th>状态</th>
<th>注册时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
if (tenants != null && !tenants.isEmpty()) {
for (User user : tenants) {
%>
<tr>
<td><%= user.getId() %></td>
<td><%= user.getUserName() %></td>
<td><%= user.getRealName() != null ? user.getRealName() : "-" %></td>
<td><%= user.getPhone() != null ? user.getPhone() : "-" %></td>
<td>
<span class="status-badge <%= "1".equals(user.getStatus()) ? "status-active" : "status-disabled" %>">
<%= "1".equals(user.getStatus()) ? "已认证" : "未认证/禁用" %>
</span>
</td>
<td><%= new java.text.SimpleDateFormat("yyyy-MM-dd").format(user.getCreateTime()) %></td>
<td>
<% if ("1".equals(user.getStatus())) { %>
<a href="${pageContext.request.contextPath}/admin/user/status?id=<%= user.getId() %>&action=disable"
class="btn btn-disable" onclick="return confirm('确定要禁用该用户吗?')">禁用</a>
<% } else { %>
<a href="${pageContext.request.contextPath}/admin/user/status?id=<%= user.getId() %>&action=enable"
class="btn btn-enable" onclick="return confirm('确定要启用该用户吗?')">启用</a>
<% } %>
<a href="${pageContext.request.contextPath}/admin/user/delete?id=<%= user.getId() %>"
class="btn btn-delete"
onclick="return confirm('确定要删除该用户吗?此操作不可恢复!')">删除</a>
</td>
</tr>
<%
}
} else {
%>
<tr>
<td colspan="7" class="empty-state">暂无租客数据</td>
</tr>
<%
}
%>
</tbody>
</table>
</div>
<!-- 房东列表 -->
<div class="user-section">
<div class="section-title">
<span class="role-icon">🏠</span> 房东列表
<span style="font-size:14px; color:#999; margin-left:10px;">共 <%= landlords != null ? landlords.size() : 0 %> 人</span>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>真实姓名</th>
<th>手机号</th>
<th>状态</th>
<th>注册时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
if (landlords != null && !landlords.isEmpty()) {
for (User user : landlords) {
%>
<tr>
<td><%= user.getId() %></td>
<td><%= user.getUserName() %></td>
<td><%= user.getRealName() != null ? user.getRealName() : "-" %></td>
<td><%= user.getPhone() != null ? user.getPhone() : "-" %></td>
<td>
<span class="status-badge <%= "1".equals(user.getStatus()) ? "status-active" : "status-disabled" %>">
<%= "1".equals(user.getStatus()) ? "已认证" : "未认证/禁用" %>
</span>
</td>
<td><%= new java.text.SimpleDateFormat("yyyy-MM-dd").format(user.getCreateTime()) %></td>
<td>
<% if ("1".equals(user.getStatus())) { %>
<a href="${pageContext.request.contextPath}/admin/user/status?id=<%= user.getId() %>&action=disable"
class="btn btn-disable" onclick="return confirm('确定要禁用该用户吗?')">禁用</a>
<% } else { %>
<a href="${pageContext.request.contextPath}/admin/user/status?id=<%= user.getId() %>&action=enable"
class="btn btn-enable" onclick="return confirm('确定要启用该用户吗?')">启用</a>
<% } %>
<a href="${pageContext.request.contextPath}/admin/user/delete?id=<%= user.getId() %>"
class="btn btn-delete"
onclick="return confirm('确定要删除该用户吗?此操作不可恢复!')">删除</a>
</td>
</tr>
<%
}
} else {
%>
<tr>
<td colspan="7" class="empty-state">暂无房东数据</td>
</tr>
<%
}
%>
</tbody>
</table>
</div>
<!-- 管理员列表 -->
<div class="user-section">
<div class="section-title">
<span class="role-icon">👑</span> 管理员列表
<span style="font-size:14px; color:#999; margin-left:10px;">共 <%= admins != null ? admins.size() : 0 %> 人</span>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>用户名</th>
<th>真实姓名</th>
<th>手机号</th>
<th>状态</th>
<th>注册时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
if (admins != null && !admins.isEmpty()) {
for (User user : admins) {
%>
<tr>
<td><%= user.getId() %></td>
<td><%= user.getUserName() %></td>
<td><%= user.getRealName() != null ? user.getRealName() : "-" %></td>
<td><%= user.getPhone() != null ? user.getPhone() : "-" %></td>
<td>
<span class="status-badge status-active">管理员</span>
</td>
<td><%= new java.text.SimpleDateFormat("yyyy-MM-dd").format(user.getCreateTime()) %></td>
<td>
<span style="color:#999;">不可操作</span>
</td>
</tr>
<%
}
} else {
%>
<tr>
<td colspan="7" class="empty-state">暂无管理员数据</td>
</tr>
<%
}
%>
</tbody>
</table>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,348 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
String error = (String) request.getAttribute("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>发布房源 - 房东后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #ff9800;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 900px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #fb8c00;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #ff9800;
text-decoration: none;
}
.main-card {
background-color: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h2 {
margin-bottom: 20px;
color: #333;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
input[type="text"],
input[type="number"],
select,
textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
input[type="file"] {
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
width: 100%;
}
textarea {
resize: vertical;
min-height: 100px;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #ff9800;
color: white;
}
.btn-primary:hover {
background-color: #fb8c00;
}
.btn-secondary {
background-color: #999;
color: white;
margin-left: 10px;
}
.btn-secondary:hover {
background-color: #777;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.row {
display: flex;
gap: 20px;
}
.row .form-group {
flex: 1;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 房东后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list">预约管理</a>
<a href="${pageContext.request.contextPath}/landlord/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a> &gt;
<span>发布房源</span>
</div>
<div class="main-card">
<h2>📝 发布新房源</h2>
<% if (error != null) { %>
<div class="error"><%= error %></div>
<% } %>
<form action="${pageContext.request.contextPath}/landlord/house/add" method="post" enctype="multipart/form-data">
<div class="form-group">
<label>房源标题 *</label>
<input type="text" name="title" placeholder="例如:朝阳区温馨一居室" required>
</div>
<div class="row">
<div class="form-group">
<label>所在区域</label>
<select name="area">
<option value="朝阳区">朝阳区</option>
<option value="海淀区">海淀区</option>
<option value="东城区">东城区</option>
<option value="西城区">西城区</option>
<option value="丰台区">丰台区</option>
</select>
</div>
<div class="form-group">
<label>户型</label>
<select name="houseType">
<option value="一室一厅">一室一厅</option>
<option value="两室一厅">两室一厅</option>
<option value="两室两厅">两室两厅</option>
<option value="三室一厅">三室一厅</option>
<option value="三室两厅">三室两厅</option>
</select>
</div>
<div class="form-group">
<label>租赁类型</label>
<select name="rentType">
<option value="0">整租</option>
<option value="1">合租</option>
</select>
</div>
</div>
<div class="form-group">
<label>详细地址</label>
<input type="text" name="address" placeholder="请输入详细地址">
</div>
<div class="row">
<div class="form-group">
<label>月租金(元)*</label>
<input type="number" name="rentPrice" placeholder="例如2500" required>
</div>
<div class="form-group">
<label>配套设施</label>
<input type="text" name="facility" placeholder="例如:空调,洗衣机,热水器,WiFi">
<div class="hint">多个设施用英文逗号分隔</div>
</div>
</div>
<!-- 房源图片上传字段 -->
<div class="form-group">
<label>房源图片</label>
<input type="file" name="images" multiple accept="image/jpeg,image/jpg,image/png,image/gif" id="imageInput">
<div class="hint">支持jpg、png、gif格式可多选单张不超过5MB</div>
</div>
<!-- 图片预览区域 -->
<div class="form-group" id="previewArea" style="display:none;">
<label>图片预览</label>
<div id="imagePreview" style="display:flex; gap:10px; flex-wrap:wrap;"></div>
</div>
<div class="form-group">
<label>房源描述</label>
<textarea name="description" placeholder="请输入房源描述信息..."></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交审核</button>
<a href="${pageContext.request.contextPath}/landlord/house/list" class="btn btn-secondary">返回</a>
</div>
<div class="hint" style="margin-top: 15px; text-align: center;">
房源提交后将进入审核状态,审核通过后即可在租客端展示
</div>
</form>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// 图片预览
document.getElementById('imageInput').addEventListener('change', function(e) {
var previewArea = document.getElementById('previewArea');
var previewContainer = document.getElementById('imagePreview');
previewContainer.innerHTML = '';
if (this.files.length > 0) {
previewArea.style.display = 'block';
for (var i = 0; i < this.files.length; i++) {
var file = this.files[i];
var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement('img');
img.src = e.target.result;
img.style.width = '100px';
img.style.height = '80px';
img.style.objectFit = 'cover';
img.style.borderRadius = '4px';
img.style.border = '1px solid #ddd';
previewContainer.appendChild(img);
};
reader.readAsDataURL(file);
}
} else {
previewArea.style.display = 'none';
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,316 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
House house = (House) request.getAttribute("house");
if (house == null) {
response.sendRedirect(request.getContextPath() + "/landlord/house/list");
return;
}
String error = (String) request.getAttribute("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>编辑房源 - 房东后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #ff9800;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 900px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #fb8c00;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #ff9800;
text-decoration: none;
}
.main-card {
background-color: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h2 {
margin-bottom: 20px;
color: #333;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
input[type="text"],
input[type="number"],
select,
textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
textarea {
resize: vertical;
min-height: 100px;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #ff9800;
color: white;
}
.btn-primary:hover {
background-color: #fb8c00;
}
.btn-secondary {
background-color: #999;
color: white;
margin-left: 10px;
}
.btn-secondary:hover {
background-color: #777;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.row {
display: flex;
gap: 20px;
}
.row .form-group {
flex: 1;
}
.status-info {
background-color: #f9f9f9;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 房东后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list">预约管理</a>
<a href="${pageContext.request.contextPath}/landlord/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a> &gt;
<span>编辑房源</span>
</div>
<div class="main-card">
<h2>✏️ 编辑房源</h2>
<% if (error != null) { %>
<div class="error"><%= error %></div>
<% } %>
<div class="status-info">
<strong>房源状态:</strong> <%= house.getStatusText() %>
<span style="margin-left: 20px;"><strong>房源编号:</strong> <%= house.getHouseNo() %></span>
</div>
<form action="${pageContext.request.contextPath}/landlord/house/edit" method="post">
<input type="hidden" name="id" value="<%= house.getId() %>">
<div class="form-group">
<label>房源标题 *</label>
<input type="text" name="title" value="<%= house.getTitle() %>" required>
</div>
<div class="row">
<div class="form-group">
<label>所在区域</label>
<select name="area">
<option value="朝阳区" <%= "朝阳区".equals(house.getArea()) ? "selected" : "" %>>朝阳区</option>
<option value="海淀区" <%= "海淀区".equals(house.getArea()) ? "selected" : "" %>>海淀区</option>
<option value="东城区" <%= "东城区".equals(house.getArea()) ? "selected" : "" %>>东城区</option>
<option value="西城区" <%= "西城区".equals(house.getArea()) ? "selected" : "" %>>西城区</option>
<option value="丰台区" <%= "丰台区".equals(house.getArea()) ? "selected" : "" %>>丰台区</option>
</select>
</div>
<div class="form-group">
<label>户型</label>
<select name="houseType">
<option value="一室一厅" <%= "一室一厅".equals(house.getHouseType()) ? "selected" : "" %>>一室一厅</option>
<option value="两室一厅" <%= "两室一厅".equals(house.getHouseType()) ? "selected" : "" %>>两室一厅</option>
<option value="两室两厅" <%= "两室两厅".equals(house.getHouseType()) ? "selected" : "" %>>两室两厅</option>
<option value="三室一厅" <%= "三室一厅".equals(house.getHouseType()) ? "selected" : "" %>>三室一厅</option>
<option value="三室两厅" <%= "三室两厅".equals(house.getHouseType()) ? "selected" : "" %>>三室两厅</option>
</select>
</div>
<div class="form-group">
<label>租赁类型</label>
<select name="rentType">
<option value="0" <%= "0".equals(house.getRentType()) ? "selected" : "" %>>整租</option>
<option value="1" <%= "1".equals(house.getRentType()) ? "selected" : "" %>>合租</option>
</select>
</div>
</div>
<div class="form-group">
<label>详细地址</label>
<input type="text" name="address" value="<%= house.getAddress() != null ? house.getAddress() : "" %>">
</div>
<div class="row">
<div class="form-group">
<label>月租金(元)*</label>
<input type="number" name="rentPrice" value="<%= house.getRentPrice() %>" required>
</div>
<div class="form-group">
<label>配套设施</label>
<input type="text" name="facility" value="<%= house.getFacility() != null ? house.getFacility() : "" %>" placeholder="例如:空调,洗衣机,热水器,WiFi">
<div class="hint">多个设施用英文逗号分隔</div>
</div>
</div>
<div class="form-group">
<label>房源图片地址</label>
<input type="text" name="imgUrl" value="<%= house.getImgUrl() != null ? house.getImgUrl() : "" %>" placeholder="例如:/images/house1.jpg,/images/house2.jpg">
<div class="hint">支持多个图片地址,用英文逗号分隔</div>
</div>
<div class="form-group">
<label>房源描述</label>
<textarea name="description" placeholder="请输入房源描述信息..."><%= house.getDescription() != null ? house.getDescription() : "" %></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">保存修改</button>
<a href="${pageContext.request.contextPath}/landlord/house/list" class="btn btn-secondary">返回</a>
</div>
</form>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,369 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<House> houseList = (List<House>) request.getAttribute("houseList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>房源管理 - 房东后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #ff9800;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #fb8c00;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #ff9800;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.add-btn {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
text-decoration: none;
border-radius: 4px;
display: inline-block;
margin-bottom: 20px;
}
.add-btn:hover {
background-color: #45a049;
}
.house-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-published {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-offline {
background-color: #ffebee;
color: #f44336;
}
.btn {
padding: 4px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
margin: 0 3px;
}
.btn-edit {
background-color: #2196F3;
color: white;
}
.btn-edit:hover {
background-color: #0b7dda;
}
.btn-delete {
background-color: #f44336;
color: white;
}
.btn-delete:hover {
background-color: #d32f2f;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 20px;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
.house-image {
width: 80px;
height: 60px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
border-radius: 4px;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.no-image {
font-size: 20px;
text-align: center;
color: #999;
}
.price {
color: #f44336;
font-weight: bold;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 房东后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/landlord/house/list" class="active">房源管理</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list">预约管理</a>
<a href="${pageContext.request.contextPath}/landlord/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/landlord/house/list">首页</a> &gt;
<span>房源管理</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📊 共有 <strong><%= totalCount %></strong> 套房源
<a href="${pageContext.request.contextPath}/landlord/house/add" class="add-btn" style="float: right;">+ 发布新房源</a>
</div>
<div class="house-table">
<%
if (houseList != null && !houseList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>房源图片</th>
<th>房源信息</th>
<th>租金</th>
<th>状态</th>
<th>发布时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (House house : houseList) {
%>
<tr>
<td>
<div class="house-image"
style="background-image: url('${pageContext.request.contextPath}/<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
</td>
<td>
<strong><%= house.getTitle() %></strong><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %> | <%= house.getHouseType() %></span>
</td>
<td class="price">¥<%= house.getRentPrice() %>/月</td>
<td>
<span class="status-badge status-<%= house.getStatus().equals("0") ? "pending" : house.getStatus().equals("1") ? "published" : "offline" %>">
<%= house.getStatusText() %>
</span>
</td>
<td><%= new java.text.SimpleDateFormat("yyyy-MM-dd").format(house.getCreateTime()) %></td>
<td>
<a href="${pageContext.request.contextPath}/landlord/house/edit?id=<%= house.getId() %>" class="btn btn-edit">编辑</a>
<a href="${pageContext.request.contextPath}/landlord/house/delete?id=<%= house.getId() %>"
class="btn btn-delete"
onclick="return confirm('确定要删除这套房源吗?删除后不可恢复!')">删除</a>
</td>
</tr>
<%
}
%>
</tbody>
</table>
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">🏠</div>
<p>还没有发布任何房源</p>
<p><a href="${pageContext.request.contextPath}/landlord/house/add">立即发布新房源 &gt;&gt;</a></p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,196 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>房东首页 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #ff9800;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #fb8c00;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
.welcome-card {
background-color: white;
border-radius: 8px;
padding: 30px;
margin-top: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
text-align: center;
}
.welcome-card h1 {
color: #333;
margin-bottom: 20px;
}
.welcome-card p {
color: #666;
margin-bottom: 30px;
}
.stats {
display: flex;
gap: 20px;
justify-content: center;
margin-top: 30px;
}
.stat-card {
background-color: #f9f9f9;
padding: 20px;
border-radius: 8px;
width: 200px;
text-align: center;
}
.stat-number {
font-size: 32px;
font-weight: bold;
color: #ff9800;
}
.stat-label {
color: #666;
margin-top: 10px;
}
.btn {
display: inline-block;
padding: 10px 20px;
background-color: #ff9800;
color: white;
text-decoration: none;
border-radius: 4px;
margin: 0 10px;
}
.btn:hover {
background-color: #fb8c00;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 房东后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list">预约管理</a>
<a href="${pageContext.request.contextPath}/landlord/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="welcome-card">
<h1>欢迎房东,<%= loginUser.getRealName() %></h1>
<p>欢迎来到租房系统房东后台,您可以管理房源、查看预约、处理订单。</p>
<div class="stats">
<div class="stat-card">
<div class="stat-number" id="houseCount">-</div>
<div class="stat-label">我的房源</div>
</div>
<div class="stat-card">
<div class="stat-number" id="reservationCount">-</div>
<div class="stat-label">待处理预约</div>
</div>
</div>
<div style="margin-top: 30px;">
<a href="${pageContext.request.contextPath}/landlord/house/list" class="btn">管理房源</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list" class="btn">查看预约</a>
</div>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// 加载统计数据
fetch('${pageContext.request.contextPath}/landlord/statistics')
.then(response => response.json())
.then(data => {
document.getElementById('houseCount').innerText = data.houseCount || 0;
document.getElementById('reservationCount').innerText = data.reservationCount || 0;
})
.catch(err => console.log('加载统计数据失败', err));
</script>
</body>
</html>

View File

@@ -0,0 +1,408 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Order, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Order> orderList = (List<Order>) request.getAttribute("orderList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>订单管理 - 房东后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #ff9800;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #fb8c00;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #ff9800;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.order-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending-pay {
background-color: #fff3e0;
color: #f39c12;
}
.status-paid {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.status-pending-sign {
background-color: #e3f2fd;
color: #2196F3;
}
.status-completed {
background-color: #e8f5e9;
color: #2e7d32;
}
.price {
color: #f44336;
font-weight: bold;
}
.btn {
padding: 5px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
margin: 0 3px;
}
.btn-confirm {
background-color: #4CAF50;
color: white;
}
.btn-confirm:hover {
background-color: #45a049;
}
.btn-disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 房东后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list">预约管理</a>
<a href="${pageContext.request.contextPath}/landlord/order/list" class="active">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/landlord/house/list">首页</a> &gt;
<span>订单管理</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📋 共有 <strong><%= totalCount %></strong> 个订单
</div>
<div class="order-table">
<%
if (orderList != null && !orderList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>订单编号</th>
<th>租客信息</th>
<th>房源信息</th>
<th>租赁时间</th>
<th>总租金</th>
<th>支付状态</th>
<th>订单状态</th>
<th>下单时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (Order order : orderList) {
House house = order.getHouse();
User tenant = order.getTenant();
%>
<tr>
<td style="font-size:12px;"><%= order.getOrderNo() %></td>
<td>
<%= tenant != null ? (tenant.getRealName() != null ? tenant.getRealName() : tenant.getUserName()) : "未知" %><br>
<span style="font-size:12px;color:#999;"><%= tenant != null ? tenant.getPhone() : "" %></span>
</td>
<td>
<strong><%= house.getTitle() %></strong><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %> | <%= house.getHouseType() %></span>
</td>
<td>
<%= sdf.format(order.getRentStartTime()) %> ~ <%= sdf.format(order.getRentEndTime()) %><br>
<span style="font-size:12px;color:#999;">共<%= order.getRentDays() %>天</span>
</td>
<td class="price">¥<%= order.getTotalRent() %></td>
<td>
<%
String payStatusClass = "";
String payStatusText = "";
if ("0".equals(order.getPayStatus())) {
payStatusClass = "pending-pay";
payStatusText = "待支付";
} else if ("1".equals(order.getPayStatus())) {
payStatusClass = "paid";
payStatusText = "已支付";
} else if ("2".equals(order.getPayStatus())) {
payStatusClass = "cancelled";
payStatusText = "已退款";
} else {
payStatusClass = "pending-pay";
payStatusText = "未知";
}
%>
<span class="status-badge status-<%= payStatusClass %>"><%= payStatusText %></span>
</td>
<td>
<%
String orderStatusClass = "";
String orderStatusText = "";
if ("0".equals(order.getStatus())) {
orderStatusClass = "pending-sign";
orderStatusText = "待签约";
} else if ("1".equals(order.getStatus())) {
orderStatusClass = "paid";
orderStatusText = "已签约";
} else if ("2".equals(order.getStatus())) {
orderStatusClass = "completed";
orderStatusText = "已结束";
} else if ("3".equals(order.getStatus())) {
orderStatusClass = "cancelled";
orderStatusText = "已取消";
} else {
orderStatusClass = "pending-sign";
orderStatusText = "未知";
}
%>
<span class="status-badge status-<%= orderStatusClass %>"><%= orderStatusText %></span>
</td>
<td><%= sdf.format(order.getCreateTime()) %></td>
<td>
<%
// 已支付且待签约状态:显示确认签约按钮
if ("1".equals(order.getPayStatus()) && "0".equals(order.getStatus())) {
%>
<a href="${pageContext.request.contextPath}/landlord/order/confirm?id=<%= order.getId() %>"
class="btn btn-confirm" onclick="return confirm('确认与租客签约吗?确认后将生成正式合同。')">确认签约</a>
<%
} else if ("1".equals(order.getStatus())) {
// 已签约
%>
<span class="btn btn-disabled">已签约</span>
<%
} else if ("2".equals(order.getStatus())) {
// 已结束
%>
<span class="btn btn-disabled">已完成</span>
<%
} else if ("3".equals(order.getStatus())) {
// 已取消
%>
<span class="btn btn-disabled">已取消</span>
<%
} else if ("0".equals(order.getPayStatus())) {
// 待支付
%>
<span class="btn btn-disabled">等待租客支付</span>
<%
}
%>
</td>
</tr>
<%
}
%>
</tbody>
</table>
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📦</div>
<p>暂无订单</p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,324 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Reservation, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"1".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Reservation> reservationList = (List<Reservation>) request.getAttribute("reservationList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>预约管理 - 房东后台</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #ff9800;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #fb8c00;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.reservation-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-confirmed {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.btn {
padding: 5px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
margin: 0 3px;
}
.btn-success {
background-color: #4CAF50;
color: white;
}
.btn-success:hover {
background-color: #45a049;
}
.btn-danger {
background-color: #f44336;
color: white;
}
.btn-danger:hover {
background-color: #d32f2f;
}
.btn-disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 房东后台</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/landlord/house/list">房源管理</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/list" class="active">预约管理</a>
<a href="${pageContext.request.contextPath}/landlord/order/list">订单管理</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/landlord/house/list">首页</a> &gt;
<span>预约管理</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📋 共有 <strong><%= totalCount %></strong> 条预约记录
</div>
<div class="reservation-table">
<%
if (reservationList != null && !reservationList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>租客信息</th>
<th>房源信息</th>
<th>预约时间</th>
<th>状态</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (Reservation reservation : reservationList) {
House house = reservation.getHouse();
User tenant = reservation.getTenant();
%>
<tr>
<td>
<%= tenant.getRealName() != null ? tenant.getRealName() : tenant.getUserName() %><br>
<span style="font-size:12px;color:#999;"><%= tenant.getPhone() %></span>
</td>
<td>
<strong><%= house.getTitle() %></strong><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %></span>
</td>
<td><%= sdf.format(reservation.getReserveTime()) %></td>
<td>
<span class="status-badge status-<%= reservation.getStatus().equals("0") ? "pending" : "confirmed" %>">
<%= reservation.getStatusText() %>
</span>
</td>
<td style="max-width:150px;">
<%= reservation.getRemark() != null ? reservation.getRemark() : "-" %>
</td>
<td>
<% if ("0".equals(reservation.getStatus())) { %>
<a href="${pageContext.request.contextPath}/landlord/reservation/handle?id=<%= reservation.getId() %>&action=confirm"
class="btn btn-success">确认</a>
<a href="${pageContext.request.contextPath}/landlord/reservation/handle?id=<%= reservation.getId() %>&action=reject"
class="btn btn-danger">拒绝</a>
<% } else { %>
<span class="btn-disabled">已处理</span>
<% } %>
</td>
</tr>
<%
}
%>
</tbody>
</table>
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📅</div>
<p>暂无预约记录</p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,418 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, com.hrs.model.entity.Collection, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Collection> collectionList = (List<Collection>) request.getAttribute("collectionList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我的收藏 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.collection-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.collection-card {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s;
position: relative;
}
.collection-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.house-image {
height: 200px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
}
.house-image .no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 14px;
}
.house-info {
padding: 15px;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
}
.house-title a {
color: #333;
text-decoration: none;
}
.house-title a:hover {
color: #4CAF50;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 8px;
}
.house-price {
color: #f44336;
font-size: 20px;
font-weight: bold;
margin: 10px 0;
}
.house-price span {
font-size: 14px;
font-weight: normal;
}
.collect-time {
font-size: 12px;
color: #999;
margin-top: 10px;
}
.delete-btn {
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(0,0,0,0.6);
color: white;
border: none;
border-radius: 50%;
width: 30px;
height: 30px;
cursor: pointer;
font-size: 18px;
transition: background-color 0.3s;
z-index: 10;
}
.delete-btn:hover {
background-color: #f44336;
}
.empty-state {
text-align: center;
padding: 80px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 20px;
font-size: 16px;
}
.empty-state a {
color: #4CAF50;
text-decoration: none;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.toast {
position: fixed;
top: 80px;
right: 20px;
padding: 12px 24px;
border-radius: 4px;
color: white;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.toast-success {
background-color: #4CAF50;
}
.toast-error {
background-color: #f44336;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list" class="active">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span>我的收藏</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📚 共收藏 <strong id="totalCount"><%= totalCount %></strong> 套房源
</div>
<div class="collection-list" id="collectionList">
<%
if (collectionList != null && !collectionList.isEmpty()) {
for (Collection collection : collectionList) {
House house = collection.getHouse();
if (house != null) {
%>
<div class="collection-card" data-id="<%= collection.getId() %>">
<div class="house-image" style="background-image: url('/HRS/<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<button class="delete-btn" onclick="deleteCollection(<%= collection.getId() %>)" title="取消收藏">×</button>
<div class="house-info">
<div class="house-title">
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">
<%= house.getTitle() %>
</a>
</div>
<div class="house-detail">
📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %>
</div>
<div class="house-price">
¥<%= house.getRentPrice() %><span>/月</span>
</div>
<div class="collect-time">
收藏时间:<%= collection.getCollectTime() %>
</div>
</div>
</div>
<%
}
}
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">❤️</div>
<p>还没有收藏任何房源</p>
<p><a href="${pageContext.request.contextPath}/user/house/list">去浏览房源 &gt;&gt;</a></p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// AJAX删除收藏
function deleteCollection(id) {
if (confirm('确定要取消收藏这套房源吗?')) {
// 发送AJAX请求
var xhr = new XMLHttpRequest();
xhr.open('GET', '${pageContext.request.contextPath}/user/collection/delete?id=' + id + '&ajax=true&from=list', true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = JSON.parse(xhr.responseText);
if (result.success) {
// 移除DOM元素
var card = document.querySelector('.collection-card[data-id="' + id + '"]');
if (card) {
card.remove();
}
// 更新计数
var totalSpan = document.getElementById('totalCount');
var currentCount = parseInt(totalSpan.innerText);
totalSpan.innerText = currentCount - 1;
// 显示成功提示
showToast('取消收藏成功', 'success');
// 检查是否还有收藏
var remainingCards = document.querySelectorAll('.collection-card');
if (remainingCards.length == 0) {
location.reload(); // 刷新页面显示空状态
}
} else {
showToast(result.msg || '取消收藏失败', 'error');
}
}
};
xhr.send();
}
}
// 显示提示消息
function showToast(msg, type) {
var toast = document.createElement('div');
toast.className = 'toast toast-' + type;
toast.innerHTML = msg;
document.body.appendChild(toast);
setTimeout(function() {
toast.remove();
}, 2000);
}
</script>
</body>
</html>

View File

@@ -0,0 +1,619 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
House house = (House) request.getAttribute("house");
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><%= house.getTitle() %> - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
/* 头部导航 */
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
/* 面包屑导航 */
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
/* 主要内容区 */
.main-content {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
/* 图片区域 */
.house-images {
padding: 20px;
border-bottom: 1px solid #eee;
}
.main-image {
width: 100%;
height: 400px;
background-color: #f0f0f0;
background-size: cover;
background-position: center;
border-radius: 8px;
margin-bottom: 15px;
}
.thumb-images {
display: flex;
gap: 10px;
overflow-x: auto;
}
.thumb {
width: 80px;
height: 60px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
border-radius: 4px;
cursor: pointer;
border: 2px solid transparent;
}
.thumb:hover {
border-color: #4CAF50;
}
.thumb.active {
border-color: #4CAF50;
}
.no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 24px;
}
/* 房源信息 */
.house-info {
padding: 20px;
}
.house-title {
font-size: 28px;
font-weight: bold;
color: #333;
margin-bottom: 15px;
}
.price-box {
background-color: #fff3e0;
padding: 15px 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.price {
font-size: 32px;
color: #f44336;
font-weight: bold;
}
.price span {
font-size: 16px;
font-weight: normal;
}
.info-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 20px;
padding: 15px 0;
border-bottom: 1px solid #eee;
}
.info-item {
text-align: center;
}
.info-label {
font-size: 12px;
color: #999;
margin-bottom: 5px;
}
.info-value {
font-size: 18px;
font-weight: bold;
color: #333;
}
.section {
margin-bottom: 25px;
}
.section-title {
font-size: 18px;
font-weight: bold;
padding-bottom: 10px;
margin-bottom: 15px;
border-bottom: 2px solid #4CAF50;
}
.facility-list {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.facility-item {
background-color: #f5f5f5;
padding: 5px 15px;
border-radius: 20px;
font-size: 14px;
color: #666;
}
.description {
line-height: 1.8;
color: #666;
background-color: #f9f9f9;
padding: 15px;
border-radius: 8px;
}
/* 房东信息 */
.landlord-card {
background-color: #f9f9f9;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.landlord-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.landlord-info {
display: flex;
align-items: center;
gap: 20px;
}
.landlord-avatar {
width: 60px;
height: 60px;
background-color: #4CAF50;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 24px;
}
.landlord-details p {
margin: 5px 0;
color: #666;
}
/* 操作按钮 */
.action-buttons {
display: flex;
gap: 15px;
margin-top: 20px;
margin-bottom: 40px;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
display: inline-block;
text-align: center;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-secondary {
background-color: #ff9800;
color: white;
}
.btn-secondary:hover {
background-color: #fb8c00;
}
.btn-outline {
background-color: transparent;
border: 1px solid #4CAF50;
color: #4CAF50;
}
.btn-outline:hover {
background-color: #4CAF50;
color: white;
}
/* 页脚 */
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
/* 消息提示 */
.message {
position: fixed;
top: 20px;
right: 20px;
padding: 12px 20px;
border-radius: 4px;
color: white;
z-index: 1000;
animation: slideIn 0.3s ease;
}
.message-success {
background-color: #4CAF50;
}
.message-error {
background-color: #f44336;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<!-- 头部导航 -->
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<!-- 面包屑导航 -->
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span><%= house.getTitle() %></span>
</div>
<div class="main-content">
<!-- 图片区域 -->
<div class="house-images">
<!-- 主图 -->
<div class="main-image" id="mainImage"
style="background-image: url('${pageContext.request.contextPath}/<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<!-- 缩略图列表 -->
<div class="thumb-images" id="thumbImages">
<%
String[] images = null;
if (house.getImgUrl() != null && !house.getImgUrl().isEmpty()) {
images = house.getImgUrl().split(",");
for (int i = 0; i < images.length; i++) {
%>
<div class="thumb" style="background-image: url('${pageContext.request.contextPath}/<%= images[i] %>');"
onclick="changeImage('${pageContext.request.contextPath}/<%= images[i] %>', this)"></div>
<%
}
}
%>
</div>
</div>
<!-- 房源信息 -->
<div class="house-info">
<div class="house-title"><%= house.getTitle() %></div>
<div class="price-box">
<span class="price">¥<%= house.getRentPrice() %><span>/月</span></span>
</div>
<div class="info-grid">
<div class="info-item">
<div class="info-label">户型</div>
<div class="info-value"><%= house.getHouseType() != null ? house.getHouseType() : "待定" %></div>
</div>
<div class="info-item">
<div class="info-label">面积</div>
<div class="info-value"><%= house.getArea() != null ? house.getArea() : "待定" %></div>
</div>
<div class="info-item">
<div class="info-label">租赁类型</div>
<div class="info-value"><%= house.getRentTypeText() %></div>
</div>
<div class="info-item">
<div class="info-label">房源编号</div>
<div class="info-value"><%= house.getHouseNo() %></div>
</div>
</div>
<div class="section">
<div class="section-title">📍 房源地址</div>
<p style="color: #666;"><%= house.getAddress() != null ? house.getAddress() : "暂无地址信息" %></p>
</div>
<div class="section">
<div class="section-title">🛋️ 配套设施</div>
<div class="facility-list">
<%
String facility = house.getFacility();
if (facility != null && !facility.isEmpty()) {
String[] facilities = facility.split(",");
for (String f : facilities) {
%>
<span class="facility-item"><%= f.trim() %></span>
<%
}
} else {
%>
<span class="facility-item">暂无设施信息</span>
<%
}
%>
</div>
</div>
<div class="section">
<div class="section-title">📝 房源描述</div>
<div class="description">
<%= house.getDescription() != null ? house.getDescription() : "暂无描述信息" %>
</div>
</div>
</div>
</div>
<!-- 房东信息 -->
<div class="landlord-card">
<div class="landlord-title">👤 房东信息</div>
<div class="landlord-info">
<div class="landlord-avatar">👨</div>
<div class="landlord-details">
<p><strong><%= house.getLandlordName() != null ? house.getLandlordName() : "房东" %></strong></p>
<p>📞 <%= house.getLandlordPhone() != null ? house.getLandlordPhone() : "暂无联系方式" %></p>
<p>⭐ 认证房东 · 已发布房源</p>
</div>
</div>
</div>
<!-- 操作按钮 -->
<div class="action-buttons">
<a href="${pageContext.request.contextPath}/user/order/create?houseId=<%= house.getId() %>"
class="btn btn-primary">💰 立即下单</a>
<button class="btn btn-secondary" onclick="makeReservation()">📅 预约看房</button>
<%
com.hrs.service.CollectionService collectionService = new com.hrs.service.impl.CollectionServiceImpl();
boolean isCollected = collectionService.isCollected(loginUser.getId(), house.getId());
if (isCollected) {
%>
<button class="btn btn-outline" onclick="cancelCollection(<%= house.getId() %>)">❤️ 已收藏</button>
<%
} else {
%>
<button class="btn btn-outline" onclick="addToCollection()">🤍 收藏房源</button>
<%
}
%>
<a href="${pageContext.request.contextPath}/user/house/list" class="btn btn-outline">🔙 返回列表</a>
</div>
</div>
<!-- 页脚 -->
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// 切换主图
function changeImage(url, element) {
document.getElementById('mainImage').style.backgroundImage = 'url(' + url + ')';
// 移除所有active类
var thumbs = document.querySelectorAll('.thumb');
thumbs.forEach(function(thumb) {
thumb.classList.remove('active');
});
// 添加active类到当前
element.classList.add('active');
}
// 预约看房
function makeReservation() {
var houseId = <%= house.getId() %>;
// 跳转到预约页面
window.location.href = '${pageContext.request.contextPath}/user/reservation/add?houseId=' + houseId;
}
// 收藏房源
function addToCollection() {
var houseId = <%= house.getId() %>;
// 发送AJAX请求收藏
var xhr = new XMLHttpRequest();
xhr.open('POST', '${pageContext.request.contextPath}/user/collection/add', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var result = xhr.responseText;
if (result == 'success') {
showMessage('收藏成功!', 'success');
// 刷新页面更新按钮状态
setTimeout(function() {
location.reload();
}, 1000);
} else if (result == 'exists') {
showMessage('您已经收藏过这套房源了', 'error');
} else {
showMessage('收藏失败,请稍后重试', 'error');
}
}
};
xhr.send('houseId=' + houseId);
}
// 取消收藏
function cancelCollection(houseId) {
if (confirm('确定要取消收藏这套房源吗?')) {
// 发送AJAX请求取消收藏
var xhr = new XMLHttpRequest();
xhr.open('GET', '${pageContext.request.contextPath}/user/collection/delete?houseId=' + houseId, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
showMessage('取消收藏成功!', 'success');
// 刷新页面更新按钮状态
setTimeout(function() {
location.reload();
}, 1000);
} else if (xhr.readyState == 4) {
showMessage('取消收藏失败,请稍后重试', 'error');
}
};
xhr.send();
}
}
// 显示提示消息
function showMessage(msg, type) {
var messageDiv = document.createElement('div');
messageDiv.className = 'message message-' + type;
messageDiv.innerHTML = msg;
document.body.appendChild(messageDiv);
setTimeout(function() {
messageDiv.remove();
}, 3000);
}
// 如果有缩略图,默认第一个高亮
var firstThumb = document.querySelector('.thumb');
if (firstThumb) {
firstThumb.classList.add('active');
}
</script>
</body>
</html>

View File

@@ -0,0 +1,412 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<House> houseList = (List<House>) request.getAttribute("houseList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>房源列表 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
/* 头部导航 */
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
/* 搜索栏 */
.search-bar {
background-color: white;
padding: 20px 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.search-form {
display: flex;
gap: 15px;
align-items: flex-end;
flex-wrap: wrap;
}
.search-group {
display: flex;
flex-direction: column;
}
.search-group label {
font-size: 12px;
color: #666;
margin-bottom: 5px;
}
.search-group input, .search-group select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 150px;
}
.search-btn {
background-color: #4CAF50;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
cursor: pointer;
}
.search-btn:hover {
background-color: #45a049;
}
.reset-btn {
background-color: #999;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
/* 房源列表 */
.house-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.house-card {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.house-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.house-image {
height: 200px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
position: relative;
}
.house-image .no-image {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 14px;
}
.house-info {
padding: 15px;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 10px;
color: #333;
}
.house-title a {
color: #333;
text-decoration: none;
}
.house-title a:hover {
color: #4CAF50;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 8px;
}
.house-price {
color: #f44336;
font-size: 22px;
font-weight: bold;
margin: 10px 0;
}
.house-price span {
font-size: 14px;
font-weight: normal;
}
.house-tags {
margin: 10px 0;
}
.tag {
display: inline-block;
background-color: #f0f0f0;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
margin-right: 5px;
color: #666;
}
.house-footer {
border-top: 1px solid #eee;
padding-top: 10px;
margin-top: 10px;
display: flex;
justify-content: space-between;
align-items: center;
}
.landlord-info {
font-size: 12px;
color: #999;
}
.btn {
padding: 5px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-outline {
background-color: transparent;
border: 1px solid #4CAF50;
color: #4CAF50;
}
.btn-outline:hover {
background-color: #4CAF50;
color: white;
}
/* 统计信息 */
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 10px;
}
/* 页脚 */
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<!-- 头部导航 -->
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<!-- 搜索栏 -->
<div class="search-bar">
<div class="container">
<form class="search-form" action="${pageContext.request.contextPath}/user/house/list" method="get">
<div class="search-group">
<label>区域</label>
<select name="area">
<option value="">全部区域</option>
<option value="朝阳区" <%= "朝阳区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>朝阳区</option>
<option value="海淀区" <%= "海淀区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>海淀区</option>
<option value="东城区" <%= "东城区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>东城区</option>
<option value="西城区" <%= "西城区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>西城区</option>
<option value="丰台区" <%= "丰台区".equals(request.getAttribute("searchArea")) ? "selected" : "" %>>丰台区</option>
</select>
</div>
<div class="search-group">
<label>最低租金(元)</label>
<input type="number" name="minPrice" placeholder="不限" value="<%= request.getAttribute("minPrice") != null ? request.getAttribute("minPrice") : "" %>">
</div>
<div class="search-group">
<label>最高租金(元)</label>
<input type="number" name="maxPrice" placeholder="不限" value="<%= request.getAttribute("maxPrice") != null ? request.getAttribute("maxPrice") : "" %>">
</div>
<button type="submit" class="search-btn">搜索</button>
<a href="${pageContext.request.contextPath}/user/house/list" class="reset-btn">重置</a>
</form>
</div>
</div>
<div class="container">
<!-- 统计信息 -->
<div class="stats">
📊 共找到 <strong><%= totalCount %></strong> 套房源
</div>
<!-- 房源列表 -->
<div class="house-list">
<%
if (houseList != null && !houseList.isEmpty()) {
for (House house : houseList) {
%>
<div class="house-card">
<div class="house-image" style="background-image: url('/HRS/<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div class="no-image">🏠 暂无图片</div>
<% } %>
</div>
<div class="house-info">
<div class="house-title">
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">
<%= house.getTitle() %>
</a>
</div>
<div class="house-detail">
📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %>
</div>
<div class="house-price">
¥<%= house.getRentPrice() %><span>/月</span>
</div>
<div class="house-tags">
<span class="tag">#<%= house.getHouseType() %></span>
<span class="tag">#<%= house.getArea() %></span>
</div>
<div class="house-footer">
<div class="landlord-info">
👤 <%= house.getLandlordName() != null ? house.getLandlordName() : "房东" %>
</div>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="btn btn-primary">
查看详情
</a>
</div>
</div>
</div>
<%
}
} else {
%>
<div class="empty-state">
<div style="font-size: 48px;">🏠</div>
<p>暂无房源,请稍后再来</p>
</div>
<%
}
%>
</div>
</div>
<!-- 页脚 -->
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,5 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
// 重定向到房源列表页
response.sendRedirect(request.getContextPath() + "/user/house/list");
%>

View File

@@ -0,0 +1,151 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房系统登录</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
.login-container {
background-color: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 360px;
}
h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #666;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
}
.role-group {
display: flex;
justify-content: space-around;
margin: 10px 0;
}
.role-group label {
display: inline;
font-weight: normal;
}
button {
width: 100%;
padding: 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.register-link {
text-align: center;
margin-top: 20px;
color: #666;
}
.register-link a {
color: #4CAF50;
text-decoration: none;
}
.register-link a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="login-container">
<h2>租房系统登录</h2>
<%-- 显示错误信息 --%>
<%
String error = (String)request.getAttribute("error");
if(error != null) {
%>
<div class="error"><%= error %></div>
<%
}
%>
<%-- 显示成功信息(如注册成功) --%>
<%
String message = (String)request.getAttribute("message");
if(message != null) {
%>
<div class="message" style="background-color:#e8f5e9; color:#2e7d32; padding:10px; border-radius:4px; margin-bottom:20px; text-align:center;">
<%= message %>
</div>
<%
}
%>
<form action="${pageContext.request.contextPath}/user/login" method="post">
<div class="form-group">
<label>用户名:</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>登录角色:</label>
<div class="role-group">
<label>
<input type="radio" name="roleType" value="0" checked> 租客
</label>
<label>
<input type="radio" name="roleType" value="1"> 房东
</label>
<label>
<input type="radio" name="roleType" value="2"> 管理员
</label>
</div>
</div>
<button type="submit">登录</button>
</form>
<div class="register-link">
还没有账号? <a href="${pageContext.request.contextPath}/user/register">立即注册</a>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,366 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.House, com.hrs.model.entity.User, java.text.SimpleDateFormat, java.util.Date" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
House house = (House) request.getAttribute("house");
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
String error = (String) request.getAttribute("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String today = sdf.format(new Date());
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>创建订单 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1000px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.main-card {
background-color: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h2 {
margin-bottom: 20px;
color: #333;
}
.house-summary {
background-color: #f9f9f9;
padding: 15px;
border-radius: 8px;
margin-bottom: 25px;
display: flex;
gap: 15px;
}
.house-img {
width: 100px;
height: 100px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
border-radius: 8px;
}
.house-info {
flex: 1;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 5px;
}
.house-price {
color: #f44336;
font-size: 20px;
font-weight: bold;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
input[type="date"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
.price-preview {
background-color: #fff3e0;
padding: 15px;
border-radius: 8px;
margin: 20px 0;
text-align: center;
}
.total-price {
font-size: 28px;
color: #f44336;
font-weight: bold;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-secondary {
background-color: #999;
color: white;
margin-left: 10px;
}
.btn-secondary:hover {
background-color: #777;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.row {
display: flex;
gap: 20px;
}
.row .form-group {
flex: 1;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">房源详情</a> &gt;
<span>创建订单</span>
</div>
<div class="main-card">
<h2>📝 创建租赁订单</h2>
<% if (error != null) { %>
<div class="error"><%= error %></div>
<% } %>
<!-- 房源信息摘要 -->
<div class="house-summary">
<div class="house-img" style="background-image: url('<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;">🏠</div>
<% } %>
</div>
<div class="house-info">
<div class="house-title"><%= house.getTitle() %></div>
<div class="house-detail">📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %></div>
<div class="house-price">¥<%= house.getRentPrice() %>/月</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/user/order/create" method="post" id="orderForm">
<input type="hidden" name="houseId" value="<%= house.getId() %>">
<div class="row">
<div class="form-group">
<label>租赁开始日期 *</label>
<input type="date" name="startDate" id="startDate" required min="<%= today %>" onchange="calculateTotal()">
</div>
<div class="form-group">
<label>租赁结束日期 *</label>
<input type="date" name="endDate" id="endDate" required min="<%= today %>" onchange="calculateTotal()">
</div>
</div>
<div class="price-preview">
<div style="margin-bottom: 10px;">预计总租金</div>
<div class="total-price" id="totalPrice">¥0.00</div>
<div class="hint">* 租金按月租比例计算,实际金额以合同为准</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary" id="submitBtn" disabled>确认下单</button>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="btn btn-secondary">返回</a>
</div>
</form>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
var monthlyRent = <%= house.getRentPrice() %>;
function calculateTotal() {
var startDate = document.getElementById('startDate').value;
var endDate = document.getElementById('endDate').value;
var submitBtn = document.getElementById('submitBtn');
var totalPriceSpan = document.getElementById('totalPrice');
if (startDate && endDate) {
var start = new Date(startDate);
var end = new Date(endDate);
if (end < start) {
totalPriceSpan.innerHTML = '¥0.00';
submitBtn.disabled = true;
return;
}
// 计算天数
var diffTime = Math.abs(end - start);
var diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
// 计算租金(按比例)
var total = (monthlyRent / 30) * diffDays;
totalPriceSpan.innerHTML = '¥' + total.toFixed(2);
submitBtn.disabled = false;
} else {
totalPriceSpan.innerHTML = '¥0.00';
submitBtn.disabled = true;
}
}
// 设置结束日期最小值跟随开始日期
document.getElementById('startDate').addEventListener('change', function() {
var endDateInput = document.getElementById('endDate');
endDateInput.min = this.value;
if (endDateInput.value && endDateInput.value < this.value) {
endDateInput.value = '';
}
calculateTotal();
});
</script>
</body>
</html>

View File

@@ -0,0 +1,435 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Order, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Order> orderList = (List<Order>) request.getAttribute("orderList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我的订单 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.order-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending-pay {
background-color: #fff3e0;
color: #f39c12;
}
.status-paid {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.status-pending-sign {
background-color: #e3f2fd;
color: #2196F3;
}
.status-completed {
background-color: #e8f5e9;
color: #2e7d32;
}
.price {
color: #f44336;
font-weight: bold;
}
.btn {
padding: 5px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
margin: 0 3px;
}
.btn-pay {
background-color: #4CAF50;
color: white;
}
.btn-pay:hover {
background-color: #45a049;
}
.btn-cancel {
background-color: #f44336;
color: white;
}
.btn-cancel:hover {
background-color: #d32f2f;
}
.btn-disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.house-link {
color: #4CAF50;
text-decoration: none;
}
.house-link:hover {
text-decoration: underline;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 20px;
}
.empty-state a {
color: #4CAF50;
text-decoration: none;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list" class="active">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span>我的订单</span>
</div>
<% if (msg != null) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📋 共有 <strong><%= totalCount %></strong> 个订单
</div>
<div class="order-table">
<%
if (orderList != null && !orderList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>订单编号</th>
<th>房源信息</th>
<th>租赁时间</th>
<th>总租金</th>
<th>支付状态</th>
<th>订单状态</th>
<th>下单时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (Order order : orderList) {
House house = order.getHouse();
%>
<tr>
<td style="font-size:12px;"><%= order.getOrderNo() %></td>
<td>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="house-link">
<strong><%= house.getTitle() %></strong>
</a><br>
<span style="font-size:12px;color:#999;"><%= house.getArea() %> | <%= house.getHouseType() %></span>
</td>
<td>
<%= sdf.format(order.getRentStartTime()) %> ~ <%= sdf.format(order.getRentEndTime()) %><br>
<span style="font-size:12px;color:#999;">共<%= order.getRentDays() %>天</span>
</td>
<td class="price">¥<%= order.getTotalRent() %></td>
<td>
<%
String payStatusClass = "";
String payStatusText = "";
if ("0".equals(order.getPayStatus())) {
payStatusClass = "pending-pay";
payStatusText = "待支付";
} else if ("1".equals(order.getPayStatus())) {
payStatusClass = "paid";
payStatusText = "已支付";
} else if ("2".equals(order.getPayStatus())) {
payStatusClass = "cancelled";
payStatusText = "已退款";
} else {
payStatusClass = "pending-pay";
payStatusText = "未知";
}
%>
<span class="status-badge status-<%= payStatusClass %>"><%= payStatusText %></span>
</td>
<td>
<%
String orderStatusClass = "";
String orderStatusText = "";
if ("0".equals(order.getStatus())) {
orderStatusClass = "pending-sign";
orderStatusText = "待签约";
} else if ("1".equals(order.getStatus())) {
orderStatusClass = "paid";
orderStatusText = "已签约";
} else if ("2".equals(order.getStatus())) {
orderStatusClass = "completed";
orderStatusText = "已结束";
} else if ("3".equals(order.getStatus())) {
orderStatusClass = "cancelled";
orderStatusText = "已取消";
} else {
orderStatusClass = "pending-sign";
orderStatusText = "未知";
}
%>
<span class="status-badge status-<%= orderStatusClass %>"><%= orderStatusText %></span>
</td>
<td><%= sdf.format(order.getCreateTime()) %></td>
<td>
<%
// 待支付状态:显示支付和取消按钮
if ("0".equals(order.getPayStatus()) && "0".equals(order.getStatus())) {
%>
<a href="${pageContext.request.contextPath}/user/order/pay?id=<%= order.getId() %>"
class="btn btn-pay" onclick="return confirm('确认支付 ¥<%= order.getTotalRent() %> 吗?')">立即支付</a>
<a href="${pageContext.request.contextPath}/user/order/cancel?id=<%= order.getId() %>"
class="btn btn-cancel" onclick="return confirm('确定要取消该订单吗?')">取消订单</a>
<%
} else if ("1".equals(order.getPayStatus()) && "0".equals(order.getStatus())) {
// 已支付待签约状态
%>
<span class="btn btn-disabled">等待房东确认</span>
<%
} else if ("1".equals(order.getStatus())) {
// 已签约
%>
<span class="btn btn-disabled">已签约</span>
<%
} else if ("2".equals(order.getStatus())) {
// 已结束
%>
<span class="btn btn-disabled">已完成</span>
<%
} else if ("3".equals(order.getStatus())) {
// 已取消
%>
<span class="btn btn-disabled">已取消</span>
<%
}
%>
</td>
</tr>
<%
}
%>
</tbody>
20
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📦</div>
<p>还没有任何订单</p>
<p><a href="${pageContext.request.contextPath}/user/house/list">去浏览房源 &gt;&gt;</a></p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

View File

@@ -0,0 +1,218 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>租房系统注册</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
padding: 20px;
}
.register-container {
background-color: white;
padding: 40px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
width: 450px;
}
h2 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #666;
font-weight: bold;
}
input[type="text"],
input[type="password"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
font-size: 14px;
}
.role-group {
display: flex;
gap: 20px;
margin: 10px 0;
}
.role-group label {
display: inline;
font-weight: normal;
}
button {
width: 100%;
padding: 12px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
button:hover {
background-color: #45a049;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 10px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.login-link {
text-align: center;
margin-top: 20px;
color: #666;
}
.login-link a {
color: #4CAF50;
text-decoration: none;
}
.login-link a:hover {
text-decoration: underline;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.required {
color: #f44336;
}
</style>
</head>
<body>
<div class="register-container">
<h2>租房系统注册</h2>
<%-- 显示错误信息 --%>
<%
String error = (String)request.getAttribute("error");
if(error != null) {
%>
<div class="error"><%= error %></div>
<%
}
%>
<%-- 显示成功信息 --%>
<%
String message = (String)request.getAttribute("message");
if(message != null) {
%>
<div class="message"><%= message %></div>
<%
}
%>
<form action="${pageContext.request.contextPath}/user/register" method="post">
<div class="form-group">
<label>用户名 <span class="required">*</span></label>
<input type="text" name="username" placeholder="请输入用户名" required>
</div>
<div class="form-group">
<label>密码 <span class="required">*</span></label>
<input type="password" name="password" placeholder="请输入密码" required>
<div class="hint">密码长度6-20位建议使用字母+数字组合</div>
</div>
<div class="form-group">
<label>确认密码 <span class="required">*</span></label>
<input type="password" name="confirmPassword" placeholder="请再次输入密码" required>
</div>
<div class="form-group">
<label>真实姓名</label>
<input type="text" name="realName" placeholder="请输入真实姓名">
</div>
<div class="form-group">
<label>手机号 <span class="required">*</span></label>
<input type="text" name="phone" placeholder="请输入11位手机号" required>
</div>
<div class="form-group">
<label>身份证号</label>
<input type="text" name="idCard" placeholder="请输入身份证号(可选)">
<div class="hint">房东认证需要提供身份证号</div>
</div>
<div class="form-group">
<label>注册角色 <span class="required">*</span></label>
<div class="role-group">
<label>
<input type="radio" name="roleType" value="0" checked> 租客
</label>
<label>
<input type="radio" name="roleType" value="1"> 房东
</label>
</div>
<div class="hint">房东需要管理员审核认证后才能发布房源</div>
</div>
<button type="submit">立即注册</button>
</form>
<div class="login-link">
已有账号? <a href="${pageContext.request.contextPath}/user/login">返回登录</a>
</div>
</div>
<script>
// 简单的前端验证
document.querySelector('form').addEventListener('submit', function(e) {
var password = document.querySelector('input[name="password"]').value;
var confirm = document.querySelector('input[name="confirmPassword"]').value;
var phone = document.querySelector('input[name="phone"]').value;
// 验证密码长度
if(password.length < 6 || password.length > 20) {
alert('密码长度应为6-20位');
e.preventDefault();
return false;
}
// 验证两次密码
if(password !== confirm) {
alert('两次输入的密码不一致');
e.preventDefault();
return false;
}
// 验证手机号
var phoneReg = /^1[3-9]\d{9}$/;
if(!phoneReg.test(phone)) {
alert('请输入正确的11位手机号');
e.preventDefault();
return false;
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,313 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
House house = (House) request.getAttribute("house");
if (house == null) {
response.sendRedirect(request.getContextPath() + "/user/house/list");
return;
}
String error = (String) request.getAttribute("error");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>预约看房 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 800px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.main-card {
background-color: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.house-summary {
background-color: #f9f9f9;
padding: 15px;
border-radius: 8px;
margin-bottom: 25px;
display: flex;
gap: 15px;
}
.house-img {
width: 100px;
height: 100px;
background-color: #e0e0e0;
background-size: cover;
background-position: center;
border-radius: 8px;
}
.house-info {
flex: 1;
}
.house-title {
font-size: 18px;
font-weight: bold;
margin-bottom: 8px;
}
.house-detail {
color: #666;
font-size: 14px;
margin-bottom: 5px;
}
.house-price {
color: #f44336;
font-size: 18px;
font-weight: bold;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #333;
}
input[type="date"],
input[type="time"],
textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
textarea {
resize: vertical;
min-height: 80px;
}
.error {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
}
.btn {
padding: 12px 30px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
text-decoration: none;
display: inline-block;
}
.btn-primary {
background-color: #4CAF50;
color: white;
}
.btn-primary:hover {
background-color: #45a049;
}
.btn-secondary {
background-color: #999;
color: white;
margin-left: 10px;
}
.btn-secondary:hover {
background-color: #777;
}
.hint {
font-size: 12px;
color: #999;
margin-top: 5px;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>">房源详情</a> &gt;
<span>预约看房</span>
</div>
<div class="main-card">
<h2 style="margin-bottom: 20px;">📅 预约看房</h2>
<% if (error != null) { %>
<div class="error"><%= error %></div>
<% } %>
<!-- 房源信息摘要 -->
<div class="house-summary">
<div class="house-img" style="background-image: url('/HRS/<%= house.getFirstImage() != null ? house.getFirstImage() : "" %>');">
<% if (house.getFirstImage() == null) { %>
<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#999;">🏠</div>
<% } %>
</div>
<div class="house-info">
<div class="house-title"><%= house.getTitle() %></div>
<div class="house-detail">📍 <%= house.getArea() %> | <%= house.getHouseType() %> | <%= house.getRentTypeText() %></div>
<div class="house-price">¥<%= house.getRentPrice() %>/月</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/user/reservation/add" method="post">
<input type="hidden" name="houseId" value="<%= house.getId() %>">
<div class="form-group">
<label>选择看房日期 *</label>
<input type="date" name="reserveDate" required min="<%= new java.text.SimpleDateFormat("yyyy-MM-dd").format(new java.util.Date()) %>">
<div class="hint">请选择您方便的看房日期</div>
</div>
<div class="form-group">
<label>选择看房时间 *</label>
<input type="time" name="reserveTime" required>
<div class="hint">建议选择 9:00-18:00 之间的时间</div>
</div>
<div class="form-group">
<label>备注信息(可选)</label>
<textarea name="remark" placeholder="请输入您的特殊需求或问题..."></textarea>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">提交预约</button>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="btn btn-secondary">返回</a>
</div>
</form>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
<script>
// 设置日期最小值
var dateInput = document.querySelector('input[type="date"]');
var today = new Date();
var yyyy = today.getFullYear();
var mm = String(today.getMonth() + 1).padStart(2, '0');
var dd = String(today.getDate()).padStart(2, '0');
dateInput.min = yyyy + '-' + mm + '-' + dd;
// 设置时间默认值
var timeInput = document.querySelector('input[type="time"]');
timeInput.value = "10:00";
</script>
</body>
</html>

View File

@@ -0,0 +1,376 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="java.util.List, java.text.SimpleDateFormat, com.hrs.model.entity.Reservation, com.hrs.model.entity.House, com.hrs.model.entity.User" %>
<%
User loginUser = (User) session.getAttribute("loginUser");
if (loginUser == null || !"0".equals(loginUser.getRoleType())) {
response.sendRedirect(request.getContextPath() + "/user/login");
return;
}
List<Reservation> reservationList = (List<Reservation>) request.getAttribute("reservationList");
Integer totalCount = (Integer) request.getAttribute("totalCount");
String msg = request.getParameter("msg");
String error = request.getParameter("error");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>我的预约 - 租房系统</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, "微软雅黑", sans-serif;
background-color: #f5f5f5;
}
.header {
background-color: #4CAF50;
color: white;
padding: 15px 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.container {
width: 1200px;
margin: 0 auto;
overflow: hidden;
}
.logo {
float: left;
font-size: 24px;
font-weight: bold;
}
.nav {
float: right;
margin-top: 5px;
}
.nav a {
color: white;
text-decoration: none;
margin-left: 20px;
padding: 5px 10px;
}
.nav a:hover, .nav a.active {
background-color: #45a049;
border-radius: 4px;
}
.user-info {
float: right;
margin-right: 20px;
font-size: 14px;
}
.breadcrumb {
background-color: white;
padding: 12px 0;
margin-bottom: 20px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.breadcrumb a {
color: #4CAF50;
text-decoration: none;
}
.stats {
background-color: white;
padding: 10px 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #666;
}
.message {
background-color: #e8f5e9;
color: #2e7d32;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
.error-message {
background-color: #ffebee;
color: #c62828;
padding: 12px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
animation: fadeOut 3s ease forwards;
}
@keyframes fadeOut {
0% { opacity: 1; }
70% { opacity: 1; }
100% { opacity: 0; display: none; }
}
.reservation-table {
background-color: white;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
th {
background-color: #f5f5f5;
font-weight: bold;
color: #333;
}
tr:hover {
background-color: #f9f9f9;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
font-weight: bold;
}
.status-pending {
background-color: #fff3e0;
color: #f39c12;
}
.status-confirmed {
background-color: #e8f5e9;
color: #4CAF50;
}
.status-cancelled {
background-color: #ffebee;
color: #f44336;
}
.status-completed {
background-color: #e3f2fd;
color: #2196F3;
}
.house-link {
color: #4CAF50;
text-decoration: none;
}
.house-link:hover {
text-decoration: underline;
}
.btn {
padding: 4px 12px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
text-decoration: none;
display: inline-block;
}
.btn-danger {
background-color: #f44336;
color: white;
}
.btn-danger:hover {
background-color: #d32f2f;
}
.btn-disabled {
background-color: #ccc;
color: #666;
cursor: not-allowed;
}
.empty-state {
text-align: center;
padding: 60px;
background-color: white;
border-radius: 8px;
color: #999;
}
.empty-state p {
margin-top: 20px;
}
.empty-state a {
color: #4CAF50;
text-decoration: none;
}
.footer {
background-color: #333;
color: white;
text-align: center;
padding: 20px;
margin-top: 40px;
}
.clearfix::after {
content: "";
clear: both;
display: table;
}
</style>
</head>
<body>
<div class="header">
<div class="container clearfix">
<div class="logo">🏠 租房系统</div>
<div class="user-info">
欢迎,<%= loginUser.getRealName() %> |
<a href="${pageContext.request.contextPath}/user/logout" style="color:white;">退出</a>
</div>
<div class="nav">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a>
<a href="${pageContext.request.contextPath}/user/collection/list">我的收藏</a>
<a href="${pageContext.request.contextPath}/user/reservation/list" class="active">我的预约</a>
<a href="${pageContext.request.contextPath}/user/order/list">我的订单</a>
</div>
</div>
</div>
<div class="container">
<div class="breadcrumb">
<a href="${pageContext.request.contextPath}/user/house/list">首页</a> &gt;
<span>我的预约</span>
</div>
<% if (msg != null && !msg.isEmpty()) { %>
<div class="message"><%= msg %></div>
<% } %>
<% if (error != null && !error.isEmpty()) { %>
<div class="error-message"><%= error %></div>
<% } %>
<div class="stats">
📋 共有 <strong><%= totalCount != null ? totalCount : 0 %></strong> 条预约记录
</div>
<div class="reservation-table">
<%
if (reservationList != null && !reservationList.isEmpty()) {
%>
<table>
<thead>
<tr>
<th>房源信息</th>
<th>预约时间</th>
<th>状态</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<%
for (Reservation reservation : reservationList) {
House house = reservation.getHouse();
if (house == null) continue;
// 安全获取状态值,避免空指针
String status = reservation.getStatus();
String statusClass = "";
String statusText = "";
if ("0".equals(status)) {
statusClass = "pending";
statusText = "待确认";
} else if ("1".equals(status)) {
statusClass = "confirmed";
statusText = "已确认";
} else if ("2".equals(status)) {
statusClass = "cancelled";
statusText = "已取消";
} else if ("3".equals(status)) {
statusClass = "completed";
statusText = "已完成";
} else {
statusClass = "pending";
statusText = "未知";
}
String remark = reservation.getRemark();
String remarkDisplay = (remark != null && !remark.trim().isEmpty()) ? remark : "-";
java.util.Date reserveTime = reservation.getReserveTime();
String reserveTimeStr = reserveTime != null ? sdf.format(reserveTime) : "-";
%>
<tr>
<td>
<a href="${pageContext.request.contextPath}/user/house/detail?id=<%= house.getId() %>" class="house-link">
<strong><%= house.getTitle() != null ? house.getTitle() : "未知房源" %></strong>
</a><br>
<span style="font-size:12px;color:#999;">
<%= house.getArea() != null ? house.getArea() : "" %>
<%= house.getRentPrice() != null ? "| ¥" + house.getRentPrice() + "/月" : "" %>
</span>
</td>
<td><%= reserveTimeStr %></td>
<td>
<span class="status-badge status-<%= statusClass %>"><%= statusText %></span>
</td>
<td style="max-width:200px; overflow:hidden; text-overflow:ellipsis;">
<%= remarkDisplay %>
</td>
<td>
<% if ("0".equals(status)) { %>
<a href="${pageContext.request.contextPath}/user/reservation/cancel?id=<%= reservation.getId() %>"
class="btn btn-danger"
onclick="return confirm('确定要取消这个预约吗?')">取消</a>
<% } else { %>
<span class="btn btn-disabled">已处理</span>
<% } %>
</td>
</tr>
<%
}
%>
</tbody>
</table>
<%
} else {
%>
<div class="empty-state">
<div style="font-size: 64px;">📅</div>
<p>还没有任何预约记录</p>
<p><a href="${pageContext.request.contextPath}/user/house/list">去浏览房源 &gt;&gt;</a></p>
</div>
<%
}
%>
</div>
</div>
<div class="footer">
<p>© 2026 租房系统 | 让租房更简单</p>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 KiB