Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
瓜皮233
Vue-购物车案例
提交
cf4a0936
V
Vue-购物车案例
项目概览
瓜皮233
/
Vue-购物车案例
与 Fork 源项目一致
Fork自
inscode / VueJS
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
V
Vue-购物车案例
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
cf4a0936
编写于
5月 04, 2023
作者:
6
642b59b229c89a1824b83ed8
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Thu May 4 04:34:00 UTC 2023 inscode
上级
9647e8a1
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
114 addition
and
139 deletion
+114
-139
src/App.vue
src/App.vue
+46
-74
src/components/Counter/Counter.vue
src/components/Counter/Counter.vue
+22
-23
src/components/Footer/Footer.vue
src/components/Footer/Footer.vue
+12
-13
src/components/Goods/Goods.vue
src/components/Goods/Goods.vue
+32
-28
src/components/Header/Header.vue
src/components/Header/Header.vue
+2
-1
未找到文件。
src/App.vue
浏览文件 @
cf4a0936
<
template
>
<!-- 应用程序容器 -->
<div
class=
"app-container"
>
<!-- 页面头部 -->
<Header
title=
"购物车案例"
></Header>
<!-- 页面主体 -->
<!--
<h1>
App 根组件
</h1>
-->
<!-- 商品列表 -->
<Goods
v-for=
"item in list"
:key=
"item.id"
:title=
"item.goods_name"
:pic=
"item.goods_img"
:chstate=
"item.goods_state"
:id=
"item.id"
:price=
"item.goods_price"
:count=
"item.goods_count"
@
good_check_change=
"updateGoodState"
></Goods>
<Footer
:full_checked=
"allChecked"
:amount=
"amt"
:counts=
"allCount"
@
changAllGoodsState=
"changeState"
></Footer>
<!-- 循环渲染每个商品 -->
<Goods
v-for=
"item in list"
:key=
"item.id"
:title=
"item.goods_name"
:pic=
"item.goods_img"
:state=
"item.goods_state"
:id=
"item.id"
:price=
"item.goods_price"
:count=
"item.goods_count"
@
change-state=
"updateGoodState"
></Goods>
<!-- 渲染底部组件 -->
<Footer
:checked=
"allChecked"
:total-amount=
"totalAmount"
:total-count=
"totalCount"
@
change-all-state=
"changeAllState"
></Footer>
</div>
</
template
>
<
script
>
import
axios
from
'
axios
'
// 导入 axios 模块
import
Header
from
'
@/components/Header/Header.vue
'
// 导入 Header 组件
import
Goods
from
'
@/components/Goods/Goods.vue
'
// 导入 Goods 组件
import
Footer
from
'
@/components/Footer/Footer.vue
'
import
bus
from
'
@/components/eventBus
'
import
axios
from
'
axios
'
;
// 引入 axios 模块
import
Header
from
'
@/components/Header/Header.vue
'
;
// 引入 Header 组件
import
Goods
from
'
@/components/Goods/Goods.vue
'
;
// 引入 Goods 组件
import
Footer
from
'
@/components/Footer/Footer.vue
'
;
// 引入 Footer 组件
import
eventBus
from
'
@/components/eventBus
'
;
// 引入事件总线实例
export
default
{
components
:
{
Header
,
// 注册 Header 组件
Goods
,
// 注册 Goods 组件
Header
,
Goods
,
Footer
,
},
data
()
{
return
{
// 初始化 list 数据为空数组
list
:
[]
list
:
[],
// 初始化商品列表为空数组
}
},
methods
:
{
// 初始化购物车
的方法,通过axios发送get请求获取
数据
// 初始化购物车数据
async
initBuyCars
()
{
// 发送异步请求获取购物车数据
const
{
data
:
res
}
=
await
axios
.
get
(
'
https://www.escook.cn/api/cart
'
);
// 判断请求是否成功
if
(
res
.
status
===
200
)
{
// 将获取到的数据赋值给组件数据的 list 属性
this
.
list
=
res
.
list
;
const
{
data
}
=
await
axios
.
get
(
'
https://www.escook.cn/api/cart
'
);
// 发送 GET 请求获取数据
if
(
data
.
status
===
200
)
{
// 判断请求是否成功
this
.
list
=
data
.
list
;
// 将获取到的数据保存到组件的 list 数据中
}
},
// 更新商品状态的方法,接收一个对象作为参数
updateGoodState
(
e
)
{
// 遍历组件数据的 list 数组
this
.
list
.
some
(
item
=>
{
// 如果当前遍历到的 item 对象的 id 和传入的参数 e 中的 id 相等
if
(
item
.
id
===
e
.
id
)
{
// 将当前遍历到的 item 对象的 goods_state 属性更新为传入的参数 e 中的 state 值
item
.
goods_state
=
e
.
state
;
// 返回 true 结束循环
return
true
;
}
});
// 更新商品状态的方法
updateGoodState
({
id
,
state
})
{
const
item
=
this
.
list
.
find
(
item
=>
item
.
id
===
id
);
// 查找到对应 id 的商品
item
.
goods_state
=
state
;
// 更新对应商品的状态
},
// 修改全选状态的方法
changeAllState
(
state
)
{
this
.
list
.
forEach
(
item
=>
item
.
goods_state
=
state
);
// 循环遍历每件商品,将其状态改为全选状态
},
changeState
(
val
){
this
.
list
.
forEach
(
item
=>
item
.
goods_state
=
val
.
value
)
}
},
computed
:{
allChecked
(){
return
this
.
list
.
every
(
item
=>
item
.
goods_state
)
computed
:
{
// 计算是否全选
allChecked
()
{
return
this
.
list
.
every
(
item
=>
item
.
goods_state
);
// 如果每件商品的状态都是已选,则返回 true
},
amt
(){
return
this
.
list
.
filter
(
item
=>
item
.
goods_state
).
reduce
((
t
,
item
)
=>
t
+=
item
.
goods_price
*
item
.
goods_count
,
0
)
// 计算选中商品的总价
totalAmount
()
{
return
this
.
list
.
filter
(
item
=>
item
.
goods_state
).
reduce
((
total
,
item
)
=>
total
=
item
.
goods_price
*
item
.
goods_count
,
0
);
// 过滤出已选商品,然后用 reduce 计算商品总价
},
// 计算选中商品的数量
totalCount
()
{
return
this
.
list
.
filter
(
item
=>
item
.
goods_state
).
reduce
((
total
,
item
)
=>
total
=
item
.
goods_count
,
0
);
// 过滤出已选商品,然后用 reduce 计算商品数量总和
},
allCount
(){
return
this
.
list
.
filter
(
item
=>
item
.
goods_state
).
reduce
((
t
,
item
)
=>
t
+=
item
.
goods_count
,
0
)
}
},
created
()
{
// 在实例创建之后请求数据
this
.
initBuyCars
()
bus
.
on
(
'
share
'
,
val
=>
{
this
.
list
.
some
(
item
=>
{
if
(
item
.
id
===
val
.
id
){
item
.
goods_count
=
val
.
value
return
true
}
})
})
}
}
// 在组件创建以后,发送请求初始化购物车数据
this
.
initBuyCars
();
// 监听事件总线的 'goods-count-change' 事件,当事件触发时更改对应商品的数量
eventBus
.
on
(
'
goods-count-change
'
,
({
id
,
value
})
=>
{
const
item
=
this
.
list
.
find
(
item
=>
item
.
id
===
id
);
// 查找到对应 id 的商品
item
.
goods_count
=
value
;
// 更新对应商品的数量
});
},
};
</
script
>
<
style
lang=
"less"
scoped
>
/* 应用程序容器样式 */
.app-container {
padding-top: 45px;
/* 顶部内边距 */
padding-bottom: 50px;
/* 底部内边距 */
}
</
style
>
src/components/Counter/Counter.vue
浏览文件 @
cf4a0936
<
template
>
<div
class=
"number-container d-flex justify-content-center align-items-center"
>
<!-- 减 1 的按钮 -->
<button
type=
"button"
class=
"btn btn-light btn-sm"
@
click=
"
sub
"
>
-
</button>
<button
type=
"button"
class=
"btn btn-light btn-sm"
@
click=
"
decrement
"
>
-
</button>
<!-- 购买的数量 -->
<span
class=
"number-box"
>
{{
num
}}
</span>
<span
class=
"number-box"
>
{{
count
}}
</span>
<!-- 加 1 的按钮 -->
<button
type=
"button"
class=
"btn btn-light btn-sm"
@
click=
"
add
"
>
+
</button>
<button
type=
"button"
class=
"btn btn-light btn-sm"
@
click=
"
increment
"
>
+
</button>
</div>
</
template
>
<
script
>
import
bus
from
'
../eventBus
'
;
import
bus
from
'
../eventBus
'
;
export
default
{
props
:{
num
:{
type
:
Number
,
default
:
1
props
:
{
count
:
{
// 组件接收的商品数量
type
:
Number
,
default
:
1
,
// 默认为1
},
count_id
:
{
// 组件接收的商品 id
type
:
Number
,
required
:
true
,
// 必须传入 id
},
count_id
:{
type
:
Number
,
// 指定 id 只接受数字类型
required
:
true
,
// 指定 id 是必须的,必须传递 id 属性
}
},
methods
:{
add
(){
bus
.
emit
(
'
share
'
,{
id
:
this
.
count_id
,
value
:
this
.
num
+
1
})
methods
:
{
// 减 1
decrement
()
{
if
(
this
.
count
<=
1
)
return
;
// 商品数量不能小于 1
bus
.
emit
(
'
goods-count-change
'
,
{
id
:
this
.
count_id
,
value
:
this
.
count
-
1
});
// 发送事件,将商品数量减1
},
// 加 1
increment
()
{
bus
.
emit
(
'
goods-count-change
'
,
{
id
:
this
.
count_id
,
value
:
this
.
count
+
1
});
// 发送事件,将商品数量加1
},
sub
(){
if
(
this
.
num
===
1
)
return
bus
.
emit
(
'
share
'
,{
id
:
this
.
count_id
,
value
:
this
.
num
-
1
})
}
},
}
}
;
</
script
>
<
style
lang=
"less"
scoped
>
...
...
src/components/Footer/Footer.vue
浏览文件 @
cf4a0936
...
...
@@ -2,43 +2,42 @@
<div
class=
"footer-container"
>
<!-- 左侧的全选 -->
<div
class=
"custom-control custom-checkbox"
>
<input
type=
"checkbox"
class=
"custom-control-input"
id=
"cbFull"
:checked=
"
full_checked"
@
change=
"changAppListGoodsSState
"
/>
<input
type=
"checkbox"
class=
"custom-control-input"
id=
"cbFull"
:checked=
"
isChecked"
@
change=
"toggleAllGoods
"
/>
<label
class=
"custom-control-label"
for=
"cbFull"
>
全选
</label>
</div>
<!-- 中间的合计 -->
<div>
<span>
合计:
</span>
<span
class=
"total-price"
>
¥
{{
amount
.
toFixed
(
2
)
}}
</span>
<span
class=
"total-price"
>
¥
{{
totalPrice
.
toFixed
(
2
)
}}
</span>
</div>
<!-- 结算按钮 -->
<button
type=
"button"
class=
"btn btn-primary btn-settle"
>
结算(
{{
counts
}}
)
</button>
<button
type=
"button"
class=
"btn btn-primary btn-settle"
>
结算(
{{
totalCheckedCount
}}
)
</button>
</div>
</
template
>
<
script
>
export
default
{
props
:{
full_c
hecked
:{
default
:
true
,
isC
hecked
:{
default
:
true
,
// 是否全选默认为 true
type
:
Boolean
,
},
amount
:{
totalPrice
:{
type
:
Number
,
default
:
0
default
:
0
// 默认总价格为 0
},
counts
:{
totalCheckedCount
:{
type
:
Number
,
default
:
0
default
:
0
// 默认选中商品数量为 0
}
},
methods
:{
changAppListGoodsSState
(
e
){
this
.
$emit
(
'
changAllGoodsState
'
,{
value
:
e
.
target
.
checked
})
// 切换所有商品的选中状态
toggleAllGoods
(
e
){
this
.
$emit
(
'
changeAllGoodsState
'
,
{
value
:
e
.
target
.
checked
});
},
}
}
</
script
>
...
...
src/components/Goods/Goods.vue
浏览文件 @
cf4a0936
...
...
@@ -4,11 +4,11 @@
<div
class=
"thumb"
>
<div
class=
"custom-control custom-checkbox"
>
<!-- 复选框 -->
<input
type=
"checkbox"
class=
"custom-control-input"
:id=
"'cb' + id"
:checked=
"
chstate"
@
change=
"g
oodsChecked"
/>
<input
type=
"checkbox"
class=
"custom-control-input"
:id=
"'cb' + id"
:checked=
"
isChecked"
@
change=
"toggleG
oodsChecked"
/>
<!-- 将复选框和图片绑定在一起,实现点击图片也可以选中商品 -->
<label
class=
"custom-control-label"
:for=
"'cb' + id"
>
<!-- 商品的缩略图 -->
<img
:src=
"
pic
"
alt=
""
/>
<img
:src=
"
thumbnail
"
alt=
""
/>
</label>
</div>
</div>
...
...
@@ -18,8 +18,9 @@
<h6
class=
"goods-title"
>
{{
title
}}
</h6>
<div
class=
"goods-info-bottom"
>
<!-- 商品价格 -->
<span
class=
"goods-price"
>
¥
{{
price
}}
</span>
<Counter
:num=
"count"
:count_id=
"id"
></Counter>
<span
class=
"goods-price"
>
¥
{{
price
.
toFixed
(
2
)
}}
</span>
<!-- 商品数量 -->
<Counter
:count=
"count"
:count_id=
"id"
></Counter>
</div>
</div>
</div>
...
...
@@ -27,10 +28,11 @@
<
script
>
import
Counter
from
'
@/components/Counter/Counter.vue
'
export
default
{
components
:{
components
:
{
Counter
,
},
},
props
:
{
// 商品标题
title
:
{
...
...
@@ -38,40 +40,41 @@ export default {
default
:
'
商品名称
'
,
// 如果没有传入 title,则默认显示 '商品名称'
},
// 商品缩略图
pic
:
{
type
:
String
,
// 指定
pic
只接受字符串类型
default
:
''
,
// 如果没有传入
pic
,则默认为空字符串
thumbnail
:
{
type
:
String
,
// 指定
thumbnail
只接受字符串类型
default
:
''
,
// 如果没有传入
thumbnail
,则默认为空字符串
},
// 商品选中状态
chstate
:
{
type
:
Boolean
,
// 指定
chstate
只接受布尔类型
default
:
true
,
// 如果没有传入
chstate
,则默认为 true
isChecked
:
{
type
:
Boolean
,
// 指定
isChecked
只接受布尔类型
default
:
true
,
// 如果没有传入
isChecked
,则默认为 true
},
// 商品 ID
id
:
{
type
:
Number
,
// 指定 id 只接受数字类型
required
:
true
,
// 指定 id 是必须的,必须传递 id 属性
},
price
:{
type
:
Number
,
required
:
true
,
// 商品价格
price
:
{
type
:
Number
,
// 指定 price 只接受数字类型
required
:
true
,
// 指定 price 是必须的,必须传递 price 属性
},
// 商品数量
count
:
{
type
:
Number
,
default
:
1
,
},
count
:{
type
:
Number
,
default
:
1
}
},
methods
:
{
//
处理商品选中状态改变的方法
g
oodsChecked
(
e
)
{
// 触发一个自定义事件,将商品 ID 和状态传递给父组件
this
.
$emit
(
'
good_check_change
'
,
{
id
:
this
.
id
,
state
:
e
.
target
.
checked
})
}
}
//
切换商品选中状态
toggleG
oodsChecked
(
e
)
{
// 触发一个自定义事件,将商品 ID 和
选中
状态传递给父组件
this
.
$emit
(
'
changeGoodsCheckedState
'
,
{
id
:
this
.
id
,
isChecked
:
e
.
target
.
checked
});
}
,
}
,
}
</
script
>
<
style
lang=
"less"
scoped
>
.goods-container {
border-top: 1px solid #efefef;
...
...
@@ -111,4 +114,5 @@ export default {
}
}
}
}
</
style
>
}
</
style
>
src/components/Header/Header.vue
浏览文件 @
cf4a0936
...
...
@@ -5,11 +5,12 @@
<
script
>
export
default
{
props
:
{
// 标题
title
:
{
type
:
String
,
// 指定 title 只接受字符串类型
default
:
'
标题
'
,
// 如果没有传入 title,则默认显示 '标题'
},
}
}
,
}
</
script
>
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录