介绍:
本文将展示如何使用 jQuery、HTML 和 CSS 来实现一个简单且实用的收货地址管理模块。从基本的 HTML 结构开始,构建出一个地址卡片的布局,接着通过 CSS 添加必要的样式,使其具有良好的视觉效果和用户体验。最后,我们将使用 jQuery 增加模块的交互性,包括编辑、删除地址、表单验证和fetch提交功能以及一个响应式的模态框。
效果图
html
html分为上导航栏、地址卡片、模态框三部分,其中上导航栏部分代码下一期文章会详细给出(实现购物车商品下拉列表),html代码如下
<!DOCTYPE html>
<html>
<head>
<title>收货地址管理</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/distpicker/dist/distpicker.min.js"></script>
</head>
<body>
<!--上导航栏 下一期提供完整代码-->
<header class="top-nav">
<div class="logo">
<p id="logo"><span class="milk-icon">🥛</span>xx牛奶</p>
</div>
<div class="search-bar">
<input type="search" placeholder="搜索...">
<button><i class="fas fa-search"></i></button>
</div>
<nav class="user-links">
<a href="#" class="user-account"><i class="fas fa-user"></i>个人中心</a>
<a class="shopping-cart" href="#"><i class="fas fa-shopping-cart"></i>去购物车结算</a>
</nav>
</header>
<!-- 侧边栏 。。。下下.期... -->
<!--收货地址信息-->
<div class="address-list-container">
<div class="add-address-header">
<button class="add-address-button" id="openAddressModal">新增收货地址</button>
<span class="address-tip">您已创建2个收货地址,最多可创建25个</span>
</div>
<div id="address-card-container">
<!-- 地址卡片 -->
<div class="address-card">
<button class="remove-address-button">x</button>
<div class="address-header">
<span class="address-name">张三</span>
<span class="address-region">北京市</span>
<span class="address-default">默认地址</span>
</div>
<div class="address-content">
<div class="property_name">
<p>收货人:</p>
<p>所在地区:</p>
<p>详细地址:</p>
<p>电话:</p>
</div>
<div class="property_value">
<p>张三</p>
<p>北京市朝阳区</p>
<p>朝阳路100号</p>
<p>12345678901</p>
</div>
</div>
<div class="button-container">
<button class="set-default"></button>
<button class="edit_button">编辑地址</button>
</div>
</div>
<div class="address-card">
<button class="remove-address-button">x</button>
<div class="address-header">
<span class="address-name">李四</span>
<span class="address-region">河南省</span>
<span class="address-default"></span>
</div>
<div class="address-content">
<div class="property_name">
<p>收货人:</p>
<p>所在地区:</p>
<p>详细地址:</p>
<p>电话:</p>
</div>
<div class="property_value">
<p>李四</p>
<p>河南省郑州市金水区</p>
<p>祭城xxx号</p>
<p>12345678901</p>
</div>
</div>
<div class="button-container">
<button class="set-default">设为默认</button>
<button class="edit_button">编辑地址</button>
</div>
</div>
<!-- 可以添加更多的地址卡片 -->
</div>
</div>
<!-- 弹窗内容 -->
<div id="addressModal" class="modal">
<div class="modal-content">
<span class="close-button">×</span>
<div class="modal-header">
添加收货地址
</div>
<form id="addressForm" class="addressForm">
<label for="recipient" class="label-required">收货人:</label><br>
<input type="hidden" id="id" value="0">
<input type="text" id="recipient" name="recipient" placeholder="请输入收货人姓名" >
<span class="error-message" id="recipient-error"></span><br><br>
<label for="province" class="label-required" >收货地区:</label><br>
<div id="distpicker" class="distpicker">
<!-- 省份选择 -->
<select id="province" name="province" ></select>
<!-- 城市选择 -->
<select id="city" name="city" ></select>
<!-- 地区选择 -->
<select id="district" name="district" ></select>
<span class="error-message" id="district-error"></span>
</div><br>
<label for="address" class="label-required">详细地址:</label><br>
<input type="text" id="address" name="address" placeholder="请输入详细地址" >
<span class="error-message" id="address-error"></span><br><br>
<label for="phone" class="label-required">电话:</label><br>
<input type="tel" id="phone" name="phone" pattern="\d{11}" placeholder="请输入11位电话号码" >
<span class="error-message" id="phone-error"></span><br><br>
<input type="submit" value="保存地址">
</form>
</div>
</div>
</body>
</html>
js
用jQuery库,实现了一些简单的功能,包括打开模态框、关闭模态框、移除地址卡片、表单验证逻辑和fetch发起服务器请求提交表单
<script type="text/javascript">
$(function (){
// 初始化Distpicker
$('#distpicker').distpicker();
//打开模态框,添加收货地址
$('#openAddressModal').click(()=>{
$('.modal-header').html("添加收货地址");
$('#addressModal').show();
})
// 关闭模态框
$('.close-button').click(()=>{
$('#addressModal').hide();
//清空表单信息
$('#addressForm').get(0).reset();
// Hide error messages
$('#recipient-error').hide();
$('#district-error').hide();
$('#address-error').hide();
$('#phone-error').hide();
});
//打开模态框,编辑地址
$('#address-card-container').on('click','.edit_button',()=>{
$('.modal-header').html("编辑收货地址");
$('#addressModal').show();
})
// 移除地址按钮逻辑
$('.remove-address-button').click(function() {
$(this).closest('.address-card').remove();
});
//表单信息验证
{
// 表单元素和错误消息元素的 jQuery 对象
const $recipient = $("#recipient");
const $address = $("#address");
const $phone = $("#phone");
const $phoneError = $('#phone-error');
const $addressError = $('#address-error');
const $recipientError = $('#recipient-error');
const $districtError = $('#district-error');
const $province = $('#province');
const $city = $('#city');
const $district = $('#district');
// 验证验证收货人
function validateRecipient() {
if (!$recipient.val().trim()) {
$recipientError.text('收货人不能为空').show();
return false;
} else {
$recipientError.hide();
return true;
}
}
//验证地区
function validateAddress() {
if (!$address.val().trim()) {
$addressError.text('详细地址不能为空').show();
return false;
} else {
$addressError.hide();
return true;
}
}
//验证电话
function validatePhone() {
if (!$phone.val().trim()) {
$phoneError.text('电话号码不能为空').show();
return false;
} else if (!$phone.val().match(/^\d{11}$/)) {
$phoneError.text('电话号码格式不正确').show();
return false;
} else {
$phoneError.hide();
return true;
}
}
//验证详细地址
function validateDistrict() {
if ($province.val() === "" || $city.val() === "" || $district.val() === "") {
$districtError.text('请将地区信息填写完整').show();
return false;
} else {
$districtError.hide();
return true;
}
}
// 绑定验证逻辑到对应的事件
$recipient.blur(validateRecipient).focus(() => $recipientError.hide());
$address.blur(validateAddress).focus(() => $addressError.hide());
$phone.blur(validatePhone).focus(() => $phoneError.hide());
$province.add($city).add($district).blur(validateDistrict).focus(() => $districtError.hide());
// 表单提交验证验证逻辑
$('#addressForm').on('submit', (event) => {
event.preventDefault(); // 阻止表单默认提交行为
// 同时执行所有验证函数
let isRecipientValid = validateRecipient();
let isAddressValid = validateAddress();
let isPhoneValid = validatePhone();
let isDistrictValid = validateDistrict();
// 检查所有验证是否通过
let isFormValid = isRecipientValid && isAddressValid && isPhoneValid && isDistrictValid;
// 如果没有错误,提交表单
if (isFormValid) {
// 获取表单数据
var formData = {
consignee: $recipient.val(),
province: $province.val(),
city: $city.val(),
district: $district.val(),
address: $address.val(),
phone: $phone.val()
};
console.log(formData)
// 发送 AJAX 请求
fetch('/web/addressServlet', {
method: 'POST', // 使用 POST 方法提交数据
headers: {
'Content-Type': 'application/json;charset=UTF-8' // 指定请求的内容类型为 JSON
},
body: JSON.stringify(formData) // 将表单数据转换为 JSON 字符串并发送
})
.then(response => response.json()) // 解析返回的 JSON 数据
.then(data => {
const errorMessage = data.message;
if (data.success) {
// 添加地址成功,可以刷新地址列表或执行其他操作
closeModal();// 关闭弹窗
// 隐藏编辑地址弹窗
setTimeout(function () {
alert("success");
}, 500); // 500 毫秒后显示 alert
} else {
// 添加地址失败,显示错误消息
// 获取后端返回的错误消息
setTimeout(function () {
alert("false");
}, 500); // 500 毫秒后显示 alert
}
})
.catch(error => console.error('Error submitting form: ', error));
}
});
}
})
</script>
css
css包含两部分,分别是上导航栏的css和地址信息部分的css。其中上导航栏部分的html只给出一部分,css全部给出,在本文章主要用于页面的整体效果,完整代码将再下一期给出
1.导航栏部分css
body, h1, h2, h3, p, figure, blockquote, dl, dd {
margin: 0;
padding: 0;
}
.milk-icon {
color: #0000FF; /* 可以设置您喜欢的颜色 */
margin-right: 8px; /* 文字和图标之间的距离 */
}
.top-nav {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #f32020;
padding: 0px 20px;
padding-bottom: 5px;
}
.logo {
display: flex;
align-items: center;
}
.logo img {
margin-right: 10px;
height: 80px;
width: 100px;
}
.search-bar {
position: relative;
}
.search-bar input[type="search"] {
padding: 5px 10px;
margin-right: -4px; /* Adjust based on the design to remove gap */
}
.search-bar button {
padding: 10px 15px; /* 根据需要调整内边距,确保与输入框等高 */
background-color: blue;
color: white;
border: none; /* 去除按钮的边框 */
cursor: pointer;
height: 30px; /* 指定一个高度,确保与输入框等高 */
position: relative;
top: 3px;
width: 55px;
}
.search-bar button:hover {
background-color: #0620b6; /* 可以选择一个更深的颜色作为悬停效果 */
border: none; /* 保持边框不变 */
}
.user-links {
display: flex;
align-items: center;
}
.user-links a {
position: relative;
right: 180px;
display: flex;
align-items: center;
margin-left: 20px;
text-decoration: none;
color: #ffffff;
}
.user-links a:hover{
cursor: pointer;
color: rgb(231, 210, 210);
border: 5px;
}
.fa-user{
color: white;
}
.shopping-cart .cart-count {
background-color: #ffffff;
color: #ef0903;
border-radius: 50%;
padding: 2px 6px;
font-size: 0.8em;
position: relative;
top: -10px;
right: -10px;
}
/* 适应不同屏幕尺寸的响应式设计 */
@media (max-width: 768px) {
.top-nav {
flex-direction: column;
}
.search-bar {
margin-top: 10px;
width: 100%;
}
.search-bar input[type="search"] {
width: calc(100% - 20px);
}
.user-links {
margin-top: 10px;
justify-content: center;
}
}
#logo{
position: relative;
left: 200px;
font-family: 'Helvetica', 'Arial', sans-serif; /* 设置字体 */
color: #ffffff;
font-size: 24px; /* 设置字体大小 */
font-weight: bold; /* 字体加粗 */
padding: 10px; /* 内边距 */
border-radius: 6px; /* 边框圆角 */
width: 200px; /* 宽度 */
margin: auto; /* 上下边距为自动,左右边距为自动,使其水平居中 */
margin-top: 5px; /* 顶部外边距 */
}
/* 购物车图标和下拉内容的容器 */
.shopping-cart {
position: relative; /* 保持不变 */
/* 其他样式保持不变 */
}
/* 隐藏下拉内容,直到鼠标悬停 */
.cart-dropdown {
display: none;
position: absolute;
right: 0px;
background-color: #fff;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1000;
border: 1px solid #ddd;
width: 300px;
top: 100%;
}
/* 悬停在购物车图标上时显示下拉内容 */
.shopping-cart:hover .cart-dropdown {
display: block;
}
.cart-items-container {
max-height: 240px; /* 可调整高度以适应三个商品项 */
overflow-y: auto; /* 当内容超出时显示垂直滚动条 */
padding-bottom: 0px; /* 留出足够的空间给结算容器 */
}
/* 下拉框内部的商品项样式保持不变 */
/* ... */
/* 下拉框内部的商品项样式 */
/* 下拉框内部的商品项样式 */
/* 下拉框内部的商品项样式 */
.cart-item {
cursor: auto;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px;
border-bottom: 1px solid #eee;
}
/* ... 其他样式不变 ... */
/* 移除按钮的样式 */
.remove-button {
padding: 2px 6px;
background-color: transparent; /* 透明背景 */
color: #007bff; /* 初始蓝色文字 */
border: none;
cursor: pointer;
font-size: 0.8em; /* 文字大小 */
transition: color 0.3s ease; /* 平滑颜色过渡效果 */
}
.remove-button:hover {
color: #0056b3; /* 悬停时的蓝色 */
text-decoration: underline; /* 添加下划线来增强悬停效果 */
}
.cart-item:hover {
background-color: #D3D3D3; /* 暗灰色背景 */
}
.cart-item .product-details span:hover {
cursor: pointer;
color: red; /* 文字标红 */
}
.product-image {
cursor: pointer;
width: 50px; /* 限制图片的宽度 */
height: auto; /* 保持图片的宽高比 */
margin-right: 10px; /* 设置图片和详情之间的间隔 */
}
.product-details {
display: flex;
flex-direction: column; /* 让详情垂直排列 */
}
.product-title {
font-size: 0.9em; /* 调整标题字体大小 */
color: #333; /* 标题颜色 */
margin-bottom: 5px; /* 与价格之间的间隔 */
}
.product-price {
color: red; /* 价格颜色 */
font-weight: bold; /* 加粗价格字体 */
margin-bottom: 5px; /* 与数量之间的间隔 */
}
.product-quantity {
font-size: 0.8em; /* 数量字体大小 */
color: #666; /* 数量颜色 */
}
/* 购物车标题的样式 */
.cart-header {
text-align: center;
padding: 5px 0;
}
.header-title {
font-size: 1em; /* 标题字体大小 */
color: red; /* 标题颜色 */
font-weight: bold; /* 加粗字体 */
background-color: #f8f8f8; /* 背景色 */
display: inline-block;
position: relative;
top: 14px; /* 根据您的实际需要调整 */
padding: 0 10px;
}
.header-underline {
height: 2px; /* 下划线厚度 */
background-color: red; /* 下划线颜色 */
margin: 0 auto 10px;}
/* 结算区域的样式 */
.checkout {
position: sticky;
bottom: 0;
background: inherit;
padding: 10px;
box-shadow: 0 -2px 2px -2px rgba(0,0,0,0.1); /* 顶部阴影效果 */
}
.total-items {
font-weight: bold; /* 加粗文字 */
color: #333; /* 文字颜色 */
}
.goto-cart-button {
position: relative;
left: 100px;
right: 5px;
padding: 10px 20px;
background-color: red; /* 按钮背景颜色 */
color: white; /* 按钮文字颜色 */
border: none; /* 去除边框 */
cursor: pointer; /* 鼠标悬停时的指针样式 */
border-radius: 4px; /* 圆角边框 */
}
.goto-cart-button:hover {
background-color: darkred; /* 鼠标悬停时的背景颜色变化 */
}
/* wehn cart is empty */
.empty_cart{
color: gray;
font-size: 12px;
text-align: center;
position: relative;
margin-top: 30px;
margin-bottom: 30px;
}
2.地址模块css代码
<style>
.address-list-container {
position: absolute;
left: 50%;
transform: translateX(-50%);
text-align: center;
width: 850px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
background: #fff;
margin-top: 20px; /* Adjust as needed */
}
.address-card h3 {
text-align: left;
color: #333;
margin-bottom: 10px;
}
.address-card p {
text-align: left;
color: #666;
line-height: 1.5;
margin-bottom: 5px;
}
.address-card {
display: flex;
flex-direction: column;
border: 1px solid #ddd;
padding: 15px;
margin-bottom: 10px;
}
.address-content {
display: flex;
}
.property_name{
display: flex;
flex-direction: column;
justify-content: space-around;
}
.property_value {
margin-left: 4px;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.property_name p{
color: gray;
text-align: right;
margin: 4px 0; /* 根据需要调整间距 */
/* 其他样式 */
}
.property_value p {
color: #5d5f60;
margin: 4px 0; /* 调整间距 */
}
.button-container {
display: flex;
justify-content: flex-end; /* 将按钮放置在右侧 */
align-items: center; /* 垂直居中对齐按钮 */
}
.set-default, .edit_button {
width: auto; /* 自适应宽度 */
margin-left: 10px; /* 为按钮之间添加一些间距 */
color: #0000FF;
background: transparent;
border: none; /* 去掉按钮的边框 */
outline: none; /* 去掉按钮的点击边框 */
cursor:pointer;
}
.set-default:hover{
color: red;
}
.edit_button:hover{
color: red;
}
.add-address-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
margin-bottom: 10px;
}
.add-address-button {
background-color: #f60;
color: white;
border: none;
padding: 10px 20px;
cursor: pointer;
font-size: 16px;
}
.add-address-button:hover {
background-color: #e55;
}
.address-tip {
position: absolute;
left: 200px;
font-size: 14px;
color: #666;
}
.address-card {
display: flex;
flex-direction: column;
/* 其他样式 */
}
.address-header {
display: flex;
align-items: center;
font-size: 18px; /* 或其他适合您设计的大小 */
margin-bottom: 15px;
color: #696565;
font-weight: bold;
/* 根据需要添加其他样式,如内边距、边距等 */
}
.address-name,
.address-region {
font-size: 18px;
margin-right: 10px; /* 添加适当的右边距 */
}
.address-default {
color: white;
background-color: #ff7125; /* 浅灰色背景 */
border-radius: 2px; /* 圆角边框 */
padding: 0px 6px; /* 内边距 */
font-size: 0.8em; /* 较小的字体尺寸 */
margin-left: 10px; /* 如果需要在默认地址和地区标签之间添加空间 */
white-space: nowrap; /* 防止文本折行 */
}
.address-card {
position: relative; /* 为子元素的绝对定位提供上下文 */
/* 其他已有的样式 */
}
.remove-address-button {
color: gray;
position: absolute; /* 绝对定位 */
top: 10px; /* 距离容器顶部的距离 */
right: 10px; /* 距离容器右侧的距离 */
background: transparent; /* 透明背景 */
border: none; /* 无边框 */
font-size: 20px; /* 字体大小,根据需要调整 */
cursor: pointer; /* 鼠标悬停时的指针形状 */
}
.remove-address-button:hover {
color: #f00; /* 鼠标悬停时的字体颜色,这里使用红色作为示例 */
}
.modal {
display: none; /* 默认隐藏弹窗 */
position: fixed; /* 固定定位 */
z-index: 2; /* 在顶层 */
left: 0;
top: 0;
width: 100%; /* 全屏宽 */
height: 100%; /* 全屏高 */
background-color: rgba(94, 92, 92, 0.4); /* 半透明背景 */
padding-top: 100px; /* 从顶部开始的内边距 */
}
.modal-content {
position: relative;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2);
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 50%;
height: 70%;
}
.close-button {
color: #aaa;
float: right;
font-size: 24px;
font-weight: bold;
position: absolute; /* Use absolute positioning */
top: -13px; /* Align to the top */
right: -5px; /* Align to the right */
padding: 10px; /* Add some space around the button for easier clicking */
cursor: pointer;
}
.close-button:hover,
.close-button:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.error-message {
display: none;
color: red;
margin-left: 5px;
font-size: 0.8em;
}
.distpicker select{
height: 30px;
border-radius: 3px;
}
.addressForm input{
padding-left: 8px;
height: 30px;
}
.addressForm label{
display: inline-block;
color: #71797f;
margin-bottom: 5px;
}
#address{
width: 325px;
}
.label-required::before {
content: "*";
color: red;
margin-right: 4px;
}
.modal-header {
padding: 10px;
background-color: #91919b;
color: white;
font-size: 20px;
text-align: center;
margin-bottom: 40px;
}
</style>
导航栏实现效果图
实现导航栏购物车商品的预览效果,也可以进行移除购物车操作,完整代码在下一期给出
前后端交互最终效果图
注:以上代码部分均为原创内容