vue + 原生input 实现购物车选择按钮 + 修改原生input样式
公司做了一个pc版的商城,我负责购物车订单这块,基于业务原因研究了购物车的逻辑实现,所以就有了以下的代码,本文用于记录,如果有兴趣的大神可以阅读源码。也可以直接使用。
静态页面
HTML部分
<template>
<div id="tree">
<div class="tree">
<div class="tree-title">
<input type="checkbox" />
全部
</div>
<div class="tree-merchant" v-for="(item, index) of data" :key="index">
<div class="tree-merchant-name">
<input type="checkbox" />
<div class="name">{{item.name}}</div>
</div>
<div class="tree-goods" v-for="(goods, indexs) of item.goods" :key="indexs">
<input type="checkbox" />
<div class="goods-name">{{goods.name}}</div>
</div>
</div>
<div class="tree-title">
<input type="checkbox" />
全部
</div>
<button @click="getGoodsCart">结 算</button>
</div>
</div>
</template>
js部分
<script>
export default {
name: "index",
data() {
return {
data: [
{
id: 1,
name: "京东自营外套店",
goods: [
{
id: 3,
name: "美的外套"
},
{
id: 4,
name: "蓝翔外套"
}
]
},
{
id: 2,
name: "京东自营衬衣店",
goods: [
{
id: 5,
name: "美的衬衣"
},
{
id: 6,
name: "蓝翔衬衣"
}
]
}
]
}
}
}
</script>
css部分
<style lang="less" scoped>
#tree {
.tree {
width: 800px;
margin: 0 auto;
.tree-title {
display: flex;
align-items: center;
padding: 10px;
background: #f3f3f3;
border: 1px solid #e9e9e9;
}
.tree-merchant {
margin: 10px 0px;
.tree-merchant-name {
display: flex;
align-items: center;
padding: 5px 10px;
border-bottom: 2px solid #999;
}
.tree-goods {
display: flex;
align-items: center;
padding: 10px;
}
}
}
// 这里开始的代码是重写input样式的css
// 思路是先把 input 全部的默认样式都去掉, 然后利用伪元素表示选中后的样式
// 缺点: 利用了 appearance: none; 可能会有兼容性问题,但是我司不考虑除谷歌内核之外的浏览器兼容
// 提示:如果要做兼容处理,请考虑其他去除默认样式的其他方式
// 作者重写的样式也是借鉴了网上的一些方法,如果侵权了请您联系我,文章的最后会有作者的QQ
input {
appearance: none;
outline: none;
margin: 0;
padding: 0;
margin-right: 10px;
}
input[type="checkbox"] {
position: relative;
width: 20px;
height: 20px;
background: #fff;
border: 1px solid #d8d8d8;
border-radius: 4px;
&:checked::after {
content: "";
position: absolute;
display: block;
width: 100%;
height: 100%;
color: #333;
text-align: center;
line-height: 18px;
background: url("https://static.98ep.com/img/BigCompany/a.png") no-repeat center;
}
}
button {
margin-top: 20px;
}
}
</style>
开始之前的小知识点(特别重要)
input type = checkbox 时绑定它的 value 与 v-model 的值是相等的或者包含在一个数组里时 input 就会变成选中状态
下面的代码实现了全选以及反选
实现代码如下:
html部分
<template>
<div id="tree">
<div class="tree">
<div class="tree-title">
<input type="checkbox" ref="businessInp" @click="getBusinessWhole" />
全部
</div>
<div class="tree-merchant" v-for="(item, index) of data" :key="index">
<div class="tree-merchant-name">
<input
type="checkbox"
v-model="business"
:value="item.id"
/>
<div class="name">{{item.name}}</div>
</div>
<div class="tree-goods" v-for="(goods, indexs) of item.goods" :key="indexs">
<input
type="checkbox"
v-model="goodsInp"
:value="goods.id"
/>
<div class="goods-name">{{goods.name}}</div>
</div>
</div>
<div class="tree-title">
<input type="checkbox" v-model="whole" @click="getBusinessWhole" />
全部
</div>
<button @click="getGoodsCart">结 算</button>
</div>
</div>
</template>
js部分
<script>
export default {
name: "index",
data() {
return {
data: [
{
id: 1,
name: "京东自营外套店",
goods: [
{
id: 3,
name: "美的外套"
},
{
id: 4,
name: "蓝翔外套"
}
]
},
{
id: 2,
name: "京东自营衬衣店",
goods: [
{
id: 5,
name: "美的衬衣"
},
{
id: 6,
name: "蓝翔衬衣"
}
]
}
],
// 存储商家的id
business: [],
// 存储商品的id
goodsInp: [],
// 用作全选的状态
whole: false
}
},
methods: {
// 获取全部商家和全部商品
getBusiness () {
if (this.$refs.businessInp.checked) {
this.data.forEach((res, index) => {
this.business.push(res.id)
res.goods.forEach((req, index) => {
this.goodsInp.push(req.id)
})
})
} else {
this.business = []
this.goodsInp = []
}
},
// 点击全选时获取全部的商家和商品
getBusinessWhole () {
if (!this.whole) {
this.$refs.businessInp.checked = true
this.whole = true
this.getBusiness()
} else {
this.$refs.businessInp.checked = false
this.whole = false
this.getBusiness()
}
},
getGoodsCart () {
console.log('选中的商品', this.goodsInp)
console.log('选中的商家', this.business)
}
}
}
</script>
<style lang="less" scoped>
#tree {
.tree {
width: 800px;
margin: 0 auto;
.tree-title {
display: flex;
align-items: center;
padding: 10px;
background: #f3f3f3;
border: 1px solid #e9e9e9;
}
.tree-merchant {
margin: 10px 0px;
.tree-merchant-name {
display: flex;
align-items: center;
padding: 5px 10px;
border-bottom: 2px solid #999;
}
.tree-goods {
display: flex;
align-items: center;
padding: 10px;
}
}
}
// 这里开始的代码是重写input样式的css
// 思路是先把 input 全部的默认样式都去掉, 然后利用伪元素表示选中后的样式
// 缺点: 利用了 appearance: none; 可能会有兼容性问题,但是我司不考虑除谷歌内核之外的浏览器兼容
// 提示:如果要做兼容处理,请考虑其他去除默认样式的其他方式
// 作者重写的样式也是借鉴了网上的一些方法,如果侵权了请您联系我,文章的最后会有作者的QQ
input {
appearance: none;
outline: none;
margin: 0;
padding: 0;
margin-right: 10px;
}
input[type="checkbox"] {
position: relative;
width: 20px;
height: 20px;
background: #fff;
border: 1px solid #d8d8d8;
border-radius: 4px;
&:checked::after {
content: "";
position: absolute;
display: block;
width: 100%;
height: 100%;
color: #333;
text-align: center;
line-height: 18px;
background: url("https://static.98ep.com/img/BigCompany/a.png") no-repeat center;
}
}
button {
margin-top: 20px;
}
}
</style>
当我们写完这些操作之后就可以看到全选和反选的效果效果图如下
需求:
1.选中一个商家,下面的商品也会跟着全部选中。
2.商家下面的商品有一个没有选中,商家的状态就不再是选中状态。
3.当有一个商家或者商品没有选中时,全选状态会被取消
4.以下是实现需求的整个逻辑代码
实现代码如下:
<template>
<div id="tree">
<div class="tree">
<div class="tree-title">
<input type="checkbox" ref="businessInp" @click="getBusinessWhole" />
全部
</div>
<div class="tree-merchant" v-for="(item, index) of data" :key="index">
<div class="tree-merchant-name">
<input type="checkbox" ref="business" v-model="business" :value="item.id" @click="getBusinessId(item, index)" />
<div class="name">{{item.name}}</div>
</div>
<div class="tree-goods" v-for="(goods, indexs) of item.goods" :key="indexs">
<input type="checkbox" :ref="`goods${goods.id}`" v-model="goodsInp" :value="goods.id" @click="getGoodsDetails(item, index, goods, indexs)" />
<div class="goods-name">{{goods.name}}</div>
</div>
</div>
<div class="tree-title">
<input type="checkbox" v-model="whole" @click="getBusinessWhole" />
全部
</div>
<button @click="getGoodsCart">结 算</button>
</div>
</div>
</template>
<script>
export default {
name: "index",
data() {
return {
data: [
{
id: 1,
name: "京东自营外套店",
goods: [
{
id: 3,
name: "美的外套"
},
{
id: 4,
name: "蓝翔外套"
}
]
},
{
id: 2,
name: "京东自营衬衣店",
goods: [
{
id: 5,
name: "美的衬衣"
},
{
id: 6,
name: "蓝翔衬衣"
}
]
}
],
business: [],
goodsInp: [],
whole: false
}
},
computed: {
getDataLength () {
return this.data.length
},
getBusinessLength () {
return this.business.length
},
getGoodsInpLength () {
return this.goodsInp.length
}
},
methods: {
// 获取全部商家和全部商品
getBusiness () {
if (this.$refs.businessInp.checked) {
this.data.forEach((res, index) => {
this.business.push(res.id)
this.duplicateRemoval()
res.goods.forEach((req, index) => {
this.goodsInp.push(req.id)
this.duplicateRemovalGoodsId()
})
})
} else {
this.business = []
this.goodsInp = []
}
},
// 点击全选时获取全部的商家和商品
getBusinessWhole () {
if (!this.whole) {
this.$refs.businessInp.checked = true
this.whole = true
this.getBusiness()
} else {
this.$refs.businessInp.checked = false
this.whole = false
this.getBusiness()
}
},
// 当前商家选中时,获取商家下的子商品
// 当前商家取消选中时,取消全选,删除取消选中的商品
getBusinessId (item, index) {
if (this.$refs.business[index].checked) {
// console.log(`选中了第${item.id}个商品`)
this.business.push(item.id)
this.duplicateRemoval()
// console.log(JSON.stringify(this.business))
// console.log('this.getDatLength', this.getDatLength)
// console.log('this.getBusinessLength', this.getBusinessLength)
// 所有的商家选中时,全选按钮选中
let isShole = this.getBusinessLength === this.getDataLength
// console.log('isShole', isShole)
if (isShole) {
this.whole = false
this.getBusinessWhole()
}
this.getGoodsId(item)
// console.log('选中的商家', this.business)
this.duplicateRemovalGoodsId()
} else {
// console.log(`取消选中第${item.id}个商家`)
this.duplicateRemovalGoodsId()
this.removeGoodsId(item)
this.filterBusiness(item.id)
this.$refs.businessInp.checked = false
this.whole = false
// console.log('取消选中', this.business)
}
},
// 去掉重复的商家
duplicateRemoval () {
if (this.getBusinessLength !== 0) {
this.business = Array.from(new Set(this.business))
}
// console.log(this.business)
},
// 商家未选中时删除商家
filterBusiness (id) {
this.business = this.business.filter(item => {
return item !== id
})
},
// 选中当前商品
getGoodsDetails (item, indexs, good, index) {
console.log(`第${good.id}个商品`)
// console.log(this.$refs[`goods${good.id}`][0].checked)
// console.log(this.$refs[`goods${good.id}`][0].value)
if (this.$refs[`goods${good.id}`][0].checked) {
this.goodsInp.push(good.id)
this.duplicateRemovalGoodsId()
console.log(JSON.stringify(this.goodsInp))
if (this.getBusinessGoodsLength(item)) {
this.$refs.business[indexs].checked = true
this.getBusinessId(item, indexs)
} else {
this.$refs.business[indexs].checked = false
}
} else {
this.duplicateRemovalGoodsId()
this.filterGetGoodsId(good.id)
this.$refs.business[indexs].checked = false
this.$refs.businessInp.checked = false
this.whole = false
if (!this.getBusinessGoodsLength(item)) {
this.business.forEach((res, index) => {
if (res === item.id) {
this.business.splice(index, 1)
}
})
}
}
},
// 获取当前商家下的所有商品
getGoodsId (item) {
// console.log('选中全部商品', item.goods)
item.goods.forEach((res, index) => {
this.goodsInp.push(res.id)
})
},
// 清空当前商家下的所有商品
removeGoodsId (item) {
item.goods.forEach((res, index) => {
this.goodsInp.forEach((req, index) => {
if (res.id === req) {
this.goodsInp.splice(index, 1)
}
})
})
},
// 商品未选中时删除该商品
filterGetGoodsId (id) {
console.log('要删除的商品id', id)
console.log('没有删除之前', JSON.stringify(this.goodsInp))
this.goodsInp = this.goodsInp.filter(item => {
return item !== id
})
console.log('已经过滤好的', JSON.stringify(this.goodsInp))
},
// 去掉重复的商品
duplicateRemovalGoodsId () {
if (this.getGoodsInpLength !== 0) {
this.goodsInp = Array.from(new Set(this.goodsInp))
}
},
// 判断当前商品是否全部选中
getBusinessGoodsLength (item) {
let totalCount = 0;
let isLen = false
console.log('已经选中的商品', this.goodsInp)
item.goods.forEach((res, index) => {
this.goodsInp.forEach((req, index) => {
if (res.id === req) {
totalCount = totalCount + 1
}
})
})
if (item.goods.length === totalCount) {
isLen = true
}
console.log('当前商家下的所有商品已经选中了', isLen)
return isLen
},
getGoodsCart () {
console.log('选中的商品', this.goodsInp)
console.log('选中的商家', this.business)
}
}
}
</script>
<style lang="less" scoped>
#tree {
.tree {
width: 800px;
margin: 0 auto;
.tree-title {
display: flex;
align-items: center;
padding: 10px;
background: #f3f3f3;
border: 1px solid #e9e9e9;
}
.tree-merchant {
margin: 10px 0px;
.tree-merchant-name {
display: flex;
align-items: center;
padding: 5px 10px;
border-bottom: 2px solid #999;
}
.tree-goods {
display: flex;
align-items: center;
padding: 10px;
}
}
}
input {
appearance: none;
outline: none;
margin: 0;
padding: 0;
margin-right: 10px;
}
input[type="checkbox"] {
position: relative;
width: 20px;
height: 20px;
background: #fff;
border: 1px solid #d8d8d8;
border-radius: 4px;
&:checked::after {
content: "";
position: absolute;
display: block;
width: 100%;
height: 100%;
color: #333;
text-align: center;
line-height: 18px;
background: url("https://static.98ep.com/img/BigCompany/a.png") no-repeat center;
}
}
button {
margin-top: 20px;
}
}
</style>
到了这里作者实现的功能也已经完成了,以下时作者完成功能的一些效果图
1.选中当前商家下的一个商品时
2.选中当前商家下的所有商品时
3.选中所有商家时
以上功能都是作者原创的,如有雷同纯属巧合,作者也是经过了一番的思考,以及写功能时经历了各种问题才写出了这个功能!
结语:作者现在是前端实习生,因为菜所以在这里记录一下,作者的QQ164205443,如果您有更好的想法或者发现了不足的地方,您可以给作者提出宝贵的建议,作者会采纳各位的建议,同时也可以问作者拿源码,也可以单纯的加作者平时吹吹水,交流交流技术也是可以的