1 根据原型写出静态页面
<div class="car">
<div class="left">
<ul>
<li class="active">推荐</li>
<li>热销</li>
<li>折扣</li>
<li>夏日必喝榜</li>
<li>经典水果</li>
<li>创意饮品</li>
<li>夏日必喝榜</li>
<li>经典水果</li>
<li>创意饮品</li>
<li>夏日必喝榜</li>
<li>经典水果</li>
<li>创意饮品</li>
</ul>
</div>
<div class="right">
<div class="goods_list">
<div class="good_item">
<div class="good_left">
<img src="./imgs/1.jpg">
</div>
<div class="good_right">
<div class="good_head">优乐美奶茶</div>
<div class="good_content">优乐美奶茶 阿萨姆风味奶茶 25g*20条/盒 早餐冲饮下午茶速溶冲泡饮</div>
<div class="rate">
<div>月售 100</div>
<div>好评率 98%</div>
</div>
<div class="add">
<div>¥21.0</div>
<div>
<span>-</span>
<span>0</span>
<span>+</span>
</div>
</div>
</div>
</div>
</div><div class="goods_list">
<div class="good_item">
<div class="good_left">
<img src="./imgs/1.jpg">
</div>
<div class="good_right">
<div class="good_head">优乐美奶茶</div>
<div class="good_content">优乐美奶茶 阿萨姆风味奶茶 25g*20条/盒 早餐冲饮下午茶速溶冲泡饮</div>
<div class="rate">
<div>月售 100</div>
<div>好评率 98%</div>
</div>
<div class="add">
<div>¥21.0</div>
<div>
<span>-</span>
<span>0</span>
<span>+</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="bottom">
<div class="left_part">
<div class="circle">
<div class="cir">
<div class="cir_inner">
<span class="inner_text">0</span>
</div>
</div>
</div>
<div class="info">
<div class="money">¥60.00</div>
<div class="fee">配送费¥5</div>
</div>
</div>
<div class="right_part">
<!-- <div >去结算</div> -->
<div >还差10元起送</div>
</div>
</div>
</div>
<script src="./index.js"/>
* {
margin: 0;
padding: 0;
}
ul li {
list-style-type: none;
}
.car {
display: flex;
.left {
font-weight: 600;
height: 896px;
width: 25%;
text-align: center;
overflow: scroll;
background: rgb(236, 236, 236);
ul {
li {
padding: 20px 0;
}
}
}
.right {
width: 75%;
.goods_list {
.good_item {
display: flex;
align-items: center;
margin: 5px 0;
.good_left {
width: 40%;
text-align: center;
img {
width: 100px;
height: 100px;
border-radius: 50%;
}
}
.good_right {
width: 60%;
font-size: 12px;
div {
margin: 5px 0;
}
.good_head {
font-weight: 800;
font-size: 16px;
}
.good_content {
width: 90%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.rate {
display: flex;
div {
margin: 0 3px;
}
}
.add {
display: flex;
justify-content: space-between;
div:first-child {
font-weight: 800;
font-size: 16px;
color: rgb(236, 109, 35);
}
div:nth-child(2) {
margin-right: 20px;
span {
margin: 0 3px;
}
span:nth-child(1),
span:nth-child(3) {
font-size: 16px;
font-weight: 800;
background: rgb(20, 132, 236);
color: #fff;
padding: 0px 6px;
border-radius: 50%;
}
}
}
}
}
}
}
.bottom {
position: fixed;
height: 60px;
width: 100%;
bottom: 0;
display: flex;
.left_part {
width: 70%;
background: black;
color: white;
display: flex;
.circle {
width: 50px;
height: 50px;
margin-left: 15px;
.cir {
width: 60px;
height: 60px;
background: black;
border-radius: 50%;
position: absolute;
top: -15px;
.cir_inner {
width: 50px;
height: 50px;
background: rgb(15, 152, 231);
border-radius: 50%;
margin: 5px;
.inner_text {
display: block;
background: red;
border-radius: 50%;
line-height: 12px;
text-align: center;
width: 12px;
height: 12px;
padding: 5px;
transform: scale(0.8);
text-shadow: none;
right: 0;
position: absolute;
}
}
}
}
.info {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-left: 20px;
.money {
font-size: 20px;
}
.fee {
font-size: 12px;
}
}
}
.right_part {
border: none;
width: 30%;
height: 100%;
background: rgb(79, 223, 79);
text-align: center;
color: #fff;
line-height: 60px;
}
}
}
2 准备数据和实现简单的逻辑封装
该步骤需要实现能在控制台操作购物车的所有的功能(增加、减少商品数量,计算价格等)
const carData = [
{
img: "./imgs/1.jpg",
name: "优乐美奶茶",
content: "优乐美奶茶 阿萨姆风味奶茶 25g*20条/盒 早餐冲饮下午茶速溶冲泡饮",
sales: "100",
rate: "98",
price: "15.5",
},
{
img: "./imgs/2.jpg",
name: "速溶冲饮袋装奶茶粉",
content: "优乐美奶茶 速溶冲饮袋装奶茶粉22g*30包 原味香芋草莓3种口味 优乐",
sales: "33",
rate: "97",
price: "10.3",
},
{
img: "./imgs/3.jpg",
name: "阿萨姆原味奶茶",
content: "统一 阿萨姆 原味奶茶 6入装奶茶饮料 300ml*6瓶/件 统一 阿萨姆 原味",
sales: "450",
rate: "85",
price: "30",
},
{
img: "./imgs/4.jpg",
name: "香飘飘奶茶",
content: "香飘飘奶茶 美味升级20杯 整箱礼盒装 原味麦香咖啡香芋4种口味1.6kg",
sales: "1000",
rate: "99",
price: "60.5",
},
{
img: "./imgs/5.jpg",
name: "速溶袋装香芋味奶茶",
content: "优乐美奶茶 速溶袋装香芋味奶茶22克*30包 早餐下午茶冲调饮品 优乐",
sales: "150",
rate: "91",
price: "14.5",
},
];
//单个商品的数据
class UIGoods {
constructor(good) {
this.data = good;
this.choose = 0; //每个商品都需要设置一个选择了多少的数量值
}
//获取到本个商品的总价格
getTotalPrice() {
return this.data.price * this.choose;
}
//判断当前商品是否被选中
ischoosed() {
return this.choose > 0;
}
//增加商品数量
increase() {
return this.choose++;
}
//减少商品数量
decrease() {
if (this.choose === 0) {
return;
}
return this.choose--;
}
}
// const goods = new UIGoods(carData[0]);
// console.log(goods);
//整个页面的数据
class UI {
constructor() {
//将单个商品数据class处理后的数据赋值
let uiGoods = [];
carData.forEach((val) => {
let ui = new UIGoods(val);
uiGoods.push(ui);
});
console.log(uiGoods[1].__proto__);
this.uiGoods = uiGoods;
//最低起送价格,每家商店的钱是不一样的,这里的数据一般通过网络获取
this.lowDeliveryPrice = 30;
//配送费
this.deliveryPrice = 6;
}
//获取配送费
getDeliveryPrice() {
return this.deliveryPrice;
}
//获取还差几元起送
//计算总价
getTotalPrice() {
let total = 0;
this.uiGoods.forEach((val, index) => {
let g = this.uiGoods[index];
// total += val.choose * val.data.price;
total += g.getTotalPrice();
});
return total;
}
//获取选择的商品数量
getSelectedNumber() {
let total = 0;
this.uiGoods.forEach((val) => {
total += val.choose;
});
return total;
}
//增加商品数量
increase(index) {
this.uiGoods[index].increase();
}
//减少商品数量
decrease(index) {
this.uiGoods[index].decrease();
}
//以下函数是为了控制页面的的显示效果做的判断
//购物车中是否含有商品
isGoodInCar() {
return this.getSelectedNumber() > 0;
}
//是否达到起送标准
isCrossDelivery() {
return this.getTotalPrice() > this.deliveryPrice;
}
}
const ui = new UI();
console.log(ui);
以下为控制台控制购物车的基本功能。
3 实现页面上的交互
通过前面两步,我们已经实现了能够在控制台进行操作购物车的逻辑。但是这个界面是需要交给用户去使用的,我们还需要将页面和第二步的逻辑结合在一起。
这一部分对上面两步骤的代码做了调整,所有源码如下:
less:
* {
margin: 0;
padding: 0;
}
ul li {
list-style-type: none;
}
.car {
display: flex;
.left {
font-weight: 600;
height: 896px;
width: 25%;
text-align: center;
overflow: scroll;
background: rgb(236, 236, 236);
ul {
li {
padding: 20px 0;
}
}
}
.right {
width: 75%;
.goods_list {
.good_item {
display: flex;
align-items: center;
margin: 5px 0;
.good_left {
width: 40%;
text-align: center;
img {
width: 100px;
height: 100px;
border-radius: 50%;
}
}
.good_right {
width: 60%;
font-size: 12px;
div {
margin: 5px 0;
}
.good_head {
font-weight: 800;
font-size: 16px;
}
.good_content {
width: 90%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.rate {
display: flex;
div {
margin: 0 3px;
}
}
.add {
display: flex;
justify-content: space-between;
.price {
font-weight: 800;
font-size: 16px;
color: rgb(236, 109, 35);
}
.btns {
margin-right: 20px;
display: flex;
div {
margin: 0 3px;
}
div:nth-child(1),
div:nth-child(3) {
font-size: 16px;
font-weight: 800;
background: rgb(20, 132, 236);
color: #fff;
padding: 0px 6px;
border-radius: 50%;
}
div:nth-child(1) {
background: rgb(235, 235, 235);
color: #000;
}
}
}
}
}
}
}
.bottom {
position: fixed;
height: 60px;
width: 100%;
bottom: 0;
display: flex;
.left_part {
width: 70%;
background: black;
color: white;
display: flex;
.circle {
width: 50px;
height: 50px;
margin-left: 15px;
.cir {
width: 60px;
height: 60px;
background: black;
border-radius: 50%;
position: absolute;
top: -15px;
.cir_inner {
width: 50px;
height: 50px;
background: rgb(15, 152, 231);
border-radius: 50%;
margin: 5px;
.inner_text {
display: block;
background: red;
border-radius: 50%;
line-height: 12px;
text-align: center;
width: 12px;
height: 12px;
padding: 5px;
transform: scale(0.8);
text-shadow: none;
right: 0;
position: absolute;
}
}
}
}
.info {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-left: 20px;
.money {
font-size: 20px;
}
.fee {
font-size: 12px;
}
}
}
.right_part {
border: none;
width: 30%;
height: 100%;
font-size: 12px;
text-align: center;
color: #fff;
line-height: 60px;
div:nth-child(1) {
background: rgb(79, 223, 79);
}
div:nth-child(2) {
background: rgb(92, 91, 91);
}
}
}
}
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./index.css">
</head>
<body>
<div class="car">
<div class="left">
<ul>
<li class="active">推荐</li>
<li>热销</li>
<li>折扣</li>
<li>夏日必喝榜</li>
<li>经典水果</li>
<li>创意饮品</li>
<li>夏日必喝榜</li>
<li>经典水果</li>
<li>创意饮品</li>
<li>夏日必喝榜</li>
<li>经典水果</li>
<li>创意饮品</li>
</ul>
</div>
<div class="right">
<div class="goods_list">
</div>
</div>
<div class="bottom">
<div class="left_part">
<div class="circle">
<div class="cir">
<div class="cir_inner">
<span class="inner_text">0</span>
</div>
</div>
</div>
<div class="info">
<div class="money">¥0.00</div>
<div class="fee">配送费¥0</div>
</div>
</div>
<div class="right_part">
<div style="display: none;">去结算</div>
<div >还差0元起送</div>
</div>
</div>
</div>
<script src="./index.js"/>
</body>
</html>
js:
const carData = [
{
img: "./imgs/1.jpg",
name: "优乐美奶茶",
content: "优乐美奶茶 阿萨姆风味奶茶 25g*20条/盒 早餐冲饮下午茶速溶冲泡饮",
sales: "100",
rate: "98",
price: "15.5",
},
{
img: "./imgs/2.jpg",
name: "速溶冲饮袋装奶茶粉",
content: "优乐美奶茶 速溶冲饮袋装奶茶粉22g*30包 原味香芋草莓3种口味 优乐",
sales: "33",
rate: "97",
price: "10.3",
},
{
img: "./imgs/3.jpg",
name: "阿萨姆原味奶茶",
content: "统一 阿萨姆 原味奶茶 6入装奶茶饮料 300ml*6瓶/件 统一 阿萨姆 原味",
sales: "450",
rate: "85",
price: "30",
},
{
img: "./imgs/4.jpg",
name: "香飘飘奶茶",
content: "香飘飘奶茶 美味升级20杯 整箱礼盒装 原味麦香咖啡香芋4种口味1.6kg",
sales: "1000",
rate: "99",
price: "60.5",
},
{
img: "./imgs/5.jpg",
name: "速溶袋装香芋味奶茶",
content: "优乐美奶茶 速溶袋装香芋味奶茶22克*30包 早餐下午茶冲调饮品 优乐",
sales: "150",
rate: "91",
price: "14.5",
},
];
//单个商品的数据
class UIGoods {
constructor(good) {
this.data = good;
this.choose = 0; //每个商品都需要设置一个选择了多少的数量值
}
//获取到本个商品的总价格
getTotalPrice() {
return this.data.price * this.choose;
}
//判断当前商品是否被选中
ischoosed() {
return this.choose > 0;
}
//增加商品数量
increase() {
return this.choose++;
}
//减少商品数量
decrease() {
if (this.choose === 0) {
return;
}
return this.choose--;
}
}
// const goods = new UIGoods(carData[0]);
// console.log(goods);
//整个页面的数据
class UIData {
constructor() {
//将单个商品数据class处理后的数据赋值
let uiGoods = [];
carData.forEach((val) => {
let ui = new UIGoods(val);
uiGoods.push(ui);
});
// console.log(uiGoods[1].__proto__);
this.uiGoods = uiGoods;
//最低起送价格,每家商店的钱是不一样的,这里的数据一般通过网络获取
this.lowDeliveryPrice = 30;
//配送费
this.deliveryPrice = 6;
}
//获取配送费
getDeliveryPrice() {
return this.deliveryPrice;
}
//获取还差几元起送
//计算总价
getTotalPrice() {
let total = 0;
this.uiGoods.forEach((val, index) => {
let g = this.uiGoods[index];
// total += val.choose * val.data.price;
total += g.getTotalPrice();
});
return total;
}
//获取选择的商品数量
getSelectedNumber() {
let total = 0;
this.uiGoods.forEach((val) => {
total += val.choose;
});
return total;
}
//增加商品数量
increase(index) {
this.uiGoods[index].increase();
}
//减少商品数量
decrease(index) {
this.uiGoods[index].decrease();
}
//以下函数是为了控制页面的的显示效果做的判断
//购物车中是否含有商品
isGoodInCar() {
return this.getSelectedNumber() > 0;
}
//是否达到起送标准
isCrossDelivery() {
return this.getTotalPrice() >= this.lowDeliveryPrice;
}
ischoosed(index) {
return this.uiGoods[index].ischoosed();
}
}
// const ui = new UIData();
// console.log(ui);
//页面操作
class UI {
constructor() {
this.uiData = new UIData();
//将需要操作的dom元素放在集合里面
this.doms = {
goodsContainer: document.querySelector(".goods_list"),
bot_totalMoney: document.querySelector(".info .money"),
bot_fee: document.querySelector(".info .fee"),
bot_dots: document.querySelector(".cir_inner .inner_text"),
bot_right: document.querySelector(".bottom .right_part"),
};
this.createHTML();
this.updateFooter();
}
//根据商品数据创建商品列表元素
createHTML() {
//这里有两种方式:1,模板字符串,2,一个一个用原生创建
let html = "";
this.uiData.uiGoods.forEach((val, index) => {
html += `
<div class="good_item">
<div class="good_left">
<img src="${val.data.img}">
</div>
<div class="good_right">
<div class="good_head">${val.data.name}</div>
<div class="good_content">${val.data.content}</div>
<div class="rate">
<div>月售 ${val.data.sales}</div>
<div>好评率 ${val.data.rate}%</div>
</div>
<div class="add">
<div class="price">¥${val.data.price}</div>
<div class='btns'>
<div index="${index}" class="icon-decline" style="display:none">-</div>
<div style="display:none">0</div>
<div index="${index}" class="icon-add">+</div>
</div>
</div>
</div>
</div>
`;
});
this.doms.goodsContainer.innerHTML = html;
}
increase(index) {
this.uiData.increase(index);
this.updateGoodsItem(index);
this.updateFooter();
}
decrease(index) {
this.uiData.decrease(index);
this.updateGoodsItem(index);
this.updateFooter();
}
//更新商品的显示状态
updateGoodsItem(index) {
let goodsDom = this.doms.goodsContainer.children[index];
if (this.uiData.ischoosed(index)) {
goodsDom.querySelector(".btns").querySelectorAll("div")[0].style.display = "block";
goodsDom.querySelector(".btns").querySelectorAll("div")[1].style.display = "block";
console.log(goodsDom.querySelector(".btns"));
} else {
console.log(goodsDom.querySelector(".btns"));
goodsDom.querySelector(".btns").querySelectorAll("div")[0].style.display = "none";
goodsDom.querySelector(".btns").querySelectorAll("div")[1].style.display = "none";
}
//设置商品数量实时显示
goodsDom.querySelector(".btns").querySelectorAll("div")[1].innerText = this.uiData.uiGoods[index].choose;
}
//更新页脚的数据
updateFooter() {
//计算配送费
this.doms.bot_fee.innerText = "配送费¥" + this.uiData.deliveryPrice;
//计算底部的总价
this.doms.bot_totalMoney.innerText = "¥" + this.uiData.getTotalPrice().toFixed(2);
//计算底部显示的选中了多少个商品
this.doms.bot_dots.innerText = this.uiData.getSelectedNumber();
//计算还差多少钱起送
if (this.uiData.isCrossDelivery()) {
this.doms.bot_right.querySelectorAll("div")[0].style.display = "block";
this.doms.bot_right.querySelectorAll("div")[1].style.display = "none";
} else {
this.doms.bot_right.querySelectorAll("div")[0].style.display = "none";
this.doms.bot_right.querySelectorAll("div")[1].style.display = "block";
this.doms.bot_right.querySelectorAll("div")[1].innerText = `还差¥${this.uiData.lowDeliveryPrice - this.uiData.getTotalPrice()}元起送`;
}
}
}
const ui = new UI();
//事件
ui.doms.goodsContainer.addEventListener("click", (e) => {
if (e.target.classList.contains("icon-add")) {
ui.increase(+e.target.getAttribute("index"));
} else if (e.target.classList.contains("icon-decline")) {
ui.decrease(+e.target.getAttribute("index"));
}
});
效果如下: