上传完整架构
This commit is contained in:
418
HRS/WebContent/jsp/user/collection_list.jsp
Normal file
418
HRS/WebContent/jsp/user/collection_list.jsp
Normal 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> >
|
||||
<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">去浏览房源 >></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>
|
||||
Reference in New Issue
Block a user