vue实现购物车功能

实现功能

CSS部分

css 复制代码
   <style>
        .tr {
            display: flex;
        }

        .th {
            margin: 10px;
            width: 20%;
            height: 50%;
        }

        .td {
            display: flex;
            margin: 10px;
            width: 20%;
            height: 100px;
            align-items: center;
        }

        .app-container .banner-box {
            border-radius: 20px;
            overflow: hidden;
            margin-bottom: 10px;
        }

        .app-container .banner-box img {
            width: 100%;
        }

        .app-container .nav-box {
            background: #ddedec;
            height: 60px;
            border-radius: 10px;
            padding-left: 20px;
            display: flex;
            align-items: center;
        }

        .app-container .nav-box .my-nav {
            display: inline-block;
            background: #5fca71;
            border-radius: 5px;
            width: 90px;
            height: 35px;
            color: white;
            text-align: center;
            line-height: 35px;
            margin-right: 10px;
        }

        .breadcrumb {
            font-size: 16px;
            color: gray;
        }

        .table {
            width: 100%;
            text-align: left;
            border-radius: 2px 2px 0 0;
            border-collapse: separate;
            border-spacing: 0;
        }

        .table img {
            width: 100px;
            height: 100px;
        }

        button {
            outline: 0;
            box-shadow: none;
            color: #fff;
            background: #d9363e;
            border-color: #d9363e;
            color: #fff;
            background: #d9363e;
            border-color: #d9363e;
            line-height: 1.5715;
            position: relative;
            display: inline-block;
            font-weight: 400;
            white-space: nowrap;
            text-align: center;
            background-image: none;
            border: 1px solid transparent;
            box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            touch-action: manipulation;
            height: 32px;
            padding: 4px 15px;
            font-size: 14px;
            border-radius: 2px;
        }

        button.pay {
            background-color: #3f85ed;
            margin-left: 20px;
        }

        .bottom {
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding-right: 20px;
            border: 1px solid #f0f0f0;
            border-top: none;
            padding-left: 20px;
        }

        .right-box {
            display: flex;
            align-items: center;
        }

        .empty {
            text-align: center;
            font-size: 30px;
        }

        /* 选中时颜色是灰色 */
        .tr.active {
            background-color: #f5f7fa;
        }
    </style>

HTML

html 复制代码
<body>
<div class="app-container" id="app">
    <!-- 顶部banner -->
    <div class="banner-box"><img src="./fruitPot/封面.png" alt=""/></div>
    <!-- 面包屑 -->
    <div class="breadcrumb">
        <span>🏠</span>
        <span>购物车</span>
    </div>
    <!-- 购物车主体 -->
    <div class="main" v-if="fruitlist.length>0">
        <div class="table">
            <!-- 头部 -->
            <div class="thead">
                <div class="tr">
                    <div class="th">选中</div>
                    <div class="th th-pic">图片</div>
                    <div class="th">单价</div>
                    <div class="th num-th">个数</div>
                    <div class="th">小计</div>
                    <div class="th">操作</div>
                </div>
            </div>
            <!-- 身体 -->
            <div class="tbody">
                <div class="tr" :class="{active:item.isChecked}" v-for="(item) in fruitlist" :key="item.id">
                    <div class="td"><input type="checkbox" v-model="item.isChecked"/></div>
                    <div class="td pot"><img :src="item.icon" alt=""/></div>
                    <div class="td">{{ item.price }}</div>
                    <div class="td">
                        <div class="my-input-number">
                            <!--disabled:禁用  -->
                            <button :disabled="item.num <=0 " class="decrease" @click="sub(item.id)"> -</button>
                            <span class="my-input__inner">{{ item.num }}</span>
                            <button class="increase" @click="add(item.id)"> +</button>
                        </div>
                    </div>
                    <div class="td">{{item.price * item.num}}</div>
                    <!-- 删除filter -->
                    <div class="td">
                        <button @click="del(item.id)">删除</button>
                    </div>
                </div>

            </div>
        </div>
        <!-- 底部 -->
        <div class="bottom">
            <!-- 全选 -->
            <label class="check-all">
                <input type="checkbox" v-model="isAll"/>全选
            </label>
            <div class="right-box">
                <!-- 所有商品总价 -->
                <span class="price-box">总价&nbsp;&nbsp;:&nbsp;&nbsp;¥&nbsp;
							<span class="price">{{totalPrice()}}</span>
						</span>
                <!-- 结算按钮 -->
                <button class="pay">结算( {{totalNum()}} )</button>
            </div>
        </div>
    </div>
    <!-- 空车 -->
    <div class="empty" v-else>🛒空空如也</div>
</div>

JS

javascript 复制代码
<script>
    <!--    初始化-->
    // const defaultArr =[ ]
    const app = new Vue({
        el: '#app',
        data: {
            // // 水果列表从本地读取
            // // JSON.parse将本地JSON格式转回去
            // 加||defaultArr是怕用户删除完,下次刷新后只剩下空空如也
            // fruitlist: JSON.parse(localStorage.getItem('list'))||defaultArr,
            fruitlist: [{
                id: 1,
                icon: './fruitPot/苹果.png',
                isChecked: true,
                num: 2,
                price: 6,
            },
                {
                    id: 2,
                    icon: './fruitPot/苹果.png',
                    isChecked: false,
                    num: 7,
                    price: 20,
                },
                {
                    id: 3,
                    icon: './fruitPot/鸭梨.png',
                    isChecked: true,
                    num: 2,
                    price: 6,
                },
            ],

            },

        // 计算属性
        computed: {
            isAll:{
                // isAll:  对象
                // get()   方法
                get(){
                    // 必须所有的小选框都选中,全选按钮才选中--every
                    // 此时的item与上面的item没关联,只是一个形参
                    return this.fruitlist.every(item => {return  item.isChecked===true})
                },
                // value是复选框的布尔值
               set(value){
               //console.log(value)  ture/false
               //基于拿到的布尔值,要让所有的小选框同步状态
                   this.fruitlist.forEach(item =>item.isChecked = value)
               }

            }
        },
        methods: {
            del(id) {
                this.fruitlist = this.fruitlist.filter(item => item.id !== id)
            },
            add(id) {
                // 1.根据id 找到数组中的对应项 --find
                const fruit = this.fruitlist.find(item => item.id === id)
                // 2.操作num数量
                fruit.num++
            },
            sub(id) {
                const fruit = this.fruitlist.find(item => item.id === id)
                fruit.num--
            },
        //     总数量  reduce
            totalNum(){
                return this.fruitlist.reduce((sum, item) => {
                    if (item.isChecked) {
                        // 选中 → 需要累加
                        return sum + item.num
                    } else {
                        // 没选中 → 不需要累加
                        return sum
                    }
                }, 0)
            },
            // 总价
            totalPrice(){
                return this.fruitlist.reduce((sum, item) => {
                    if (item.isChecked) {
                        return sum + item.num * item.price
                    } else {
                        return sum
                    }
                }, 0)
            },
        },
    //     监视数据变化
        watch:{
            fruitlist:{
                deep:true,
                handler(newValue){
                    console.log(newValue)
                //     需要将变化后的newValue存入本地(转JSON)
                    localStorage.setItem('list',JSON.stringify(newValue))
                }
            }
        }
    })
</script>

全部代码

javascript 复制代码
<!-- 标签\watch\methods -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>水果购物车</title>
    <style>
        .tr {
            display: flex;
        }

        .th {
            margin: 10px;
            width: 20%;
            height: 50%;
        }

        .td {
            display: flex;
            margin: 10px;
            width: 20%;
            height: 100px;
            align-items: center;
        }

        .app-container .banner-box {
            border-radius: 20px;
            overflow: hidden;
            margin-bottom: 10px;
        }

        .app-container .banner-box img {
            width: 100%;
        }

        .app-container .nav-box {
            background: #ddedec;
            height: 60px;
            border-radius: 10px;
            padding-left: 20px;
            display: flex;
            align-items: center;
        }

        .app-container .nav-box .my-nav {
            display: inline-block;
            background: #5fca71;
            border-radius: 5px;
            width: 90px;
            height: 35px;
            color: white;
            text-align: center;
            line-height: 35px;
            margin-right: 10px;
        }

        .breadcrumb {
            font-size: 16px;
            color: gray;
        }

        .table {
            width: 100%;
            text-align: left;
            border-radius: 2px 2px 0 0;
            border-collapse: separate;
            border-spacing: 0;
        }

        .table img {
            width: 100px;
            height: 100px;
        }

        button {
            outline: 0;
            box-shadow: none;
            color: #fff;
            background: #d9363e;
            border-color: #d9363e;
            color: #fff;
            background: #d9363e;
            border-color: #d9363e;
            line-height: 1.5715;
            position: relative;
            display: inline-block;
            font-weight: 400;
            white-space: nowrap;
            text-align: center;
            background-image: none;
            border: 1px solid transparent;
            box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
            cursor: pointer;
            transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            touch-action: manipulation;
            height: 32px;
            padding: 4px 15px;
            font-size: 14px;
            border-radius: 2px;
        }

        button.pay {
            background-color: #3f85ed;
            margin-left: 20px;
        }

        .bottom {
            height: 60px;
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding-right: 20px;
            border: 1px solid #f0f0f0;
            border-top: none;
            padding-left: 20px;
        }

        .right-box {
            display: flex;
            align-items: center;
        }

        .empty {
            text-align: center;
            font-size: 30px;
        }

        /* 选中时颜色是灰色 */
        .tr.active {
            background-color: #f5f7fa;
        }
    </style>
</head>
<body>
<div class="app-container" id="app">
    <!-- 顶部banner -->
    <div class="banner-box"><img src="./fruitPot/封面.png" alt=""/></div>
    <!-- 面包屑 -->
    <div class="breadcrumb">
        <span>🏠</span>
        <span>购物车</span>
    </div>
    <!-- 购物车主体 -->
    <div class="main" v-if="fruitlist.length>0">
        <div class="table">
            <!-- 头部 -->
            <div class="thead">
                <div class="tr">
                    <div class="th">选中</div>
                    <div class="th th-pic">图片</div>
                    <div class="th">单价</div>
                    <div class="th num-th">个数</div>
                    <div class="th">小计</div>
                    <div class="th">操作</div>
                </div>
            </div>
            <!-- 身体 -->
            <div class="tbody">
                <div class="tr" :class="{active:item.isChecked}" v-for="(item) in fruitlist" :key="item.id">
                    <div class="td"><input type="checkbox" v-model="item.isChecked"/></div>
                    <div class="td pot"><img :src="item.icon" alt=""/></div>
                    <div class="td">{{ item.price }}</div>
                    <div class="td">
                        <div class="my-input-number">
                            <!--disabled:禁用  -->
                            <button :disabled="item.num <=0 " class="decrease" @click="sub(item.id)"> -</button>
                            <span class="my-input__inner">{{ item.num }}</span>
                            <button class="increase" @click="add(item.id)"> +</button>
                        </div>
                    </div>
                    <div class="td">{{item.price * item.num}}</div>
                    <!-- 删除filter -->
                    <div class="td">
                        <button @click="del(item.id)">删除</button>
                    </div>
                </div>

            </div>
        </div>
        <!-- 底部 -->
        <div class="bottom">
            <!-- 全选 -->
            <label class="check-all">
                <input type="checkbox" v-model="isAll"/>全选
            </label>
            <div class="right-box">
                <!-- 所有商品总价 -->
                <span class="price-box">总价&nbsp;&nbsp;:&nbsp;&nbsp;¥&nbsp;
							<span class="price">{{totalPrice()}}</span>
						</span>
                <!-- 结算按钮 -->
                <button class="pay">结算( {{totalNum()}} )</button>
            </div>
        </div>
    </div>
    <!-- 空车 -->
    <div class="empty" v-else>🛒空空如也</div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.7.10/dist/vue.js"></script>

<script>
    <!--    初始化-->
    // const defaultArr =[ ]
    const app = new Vue({
        el: '#app',
        data: {
            // // 水果列表从本地读取
            // // JSON.parse将本地JSON格式转回去
            // 加||defaultArr是怕用户删除完,下次刷新后只剩下空空如也
            // fruitlist: JSON.parse(localStorage.getItem('list'))||defaultArr,
            fruitlist: [{
                id: 1,
                icon: './fruitPot/苹果.png',
                isChecked: true,
                num: 2,
                price: 6,
            },
                {
                    id: 2,
                    icon: './fruitPot/苹果.png',
                    isChecked: false,
                    num: 7,
                    price: 20,
                },
                {
                    id: 3,
                    icon: './fruitPot/鸭梨.png',
                    isChecked: true,
                    num: 2,
                    price: 6,
                },
            ],

            },

        // 计算属性
        computed: {
            isAll:{
                // isAll:  对象
                // get()   方法
                get(){
                    // 必须所有的小选框都选中,全选按钮才选中--every
                    // 此时的item与上面的item没关联,只是一个形参
                    return this.fruitlist.every(item => {return  item.isChecked===true})
                },
                // value是复选框的布尔值
               set(value){
               //console.log(value)  ture/false
               //基于拿到的布尔值,要让所有的小选框同步状态
                   this.fruitlist.forEach(item =>item.isChecked = value)
               }

            }
        },
        methods: {
            del(id) {
                this.fruitlist = this.fruitlist.filter(item => item.id !== id)
            },
            add(id) {
                // 1.根据id 找到数组中的对应项 --find
                const fruit = this.fruitlist.find(item => item.id === id)
                // 2.操作num数量
                fruit.num++
            },
            sub(id) {
                const fruit = this.fruitlist.find(item => item.id === id)
                fruit.num--
            },
        //     总数量  reduce
            totalNum(){
                return this.fruitlist.reduce((sum, item) => {
                    if (item.isChecked) {
                        // 选中 → 需要累加
                        return sum + item.num
                    } else {
                        // 没选中 → 不需要累加
                        return sum
                    }
                }, 0)
            },
            // 总价
            totalPrice(){
                return this.fruitlist.reduce((sum, item) => {
                    if (item.isChecked) {
                        return sum + item.num * item.price
                    } else {
                        return sum
                    }
                }, 0)
            },
        },
    //     监视数据变化
        watch:{
            fruitlist:{
                deep:true,
                handler(newValue){
                    console.log(newValue)
                //     需要将变化后的newValue存入本地(转JSON)
                    localStorage.setItem('list',JSON.stringify(newValue))
                }
            }
        }
    })
</script>
</body>
</html>
相关推荐
学习前端的小z4 分钟前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript
彭世瑜28 分钟前
ts: TypeScript跳过检查/忽略类型检查
前端·javascript·typescript
FØund40429 分钟前
antd form.setFieldsValue问题总结
前端·react.js·typescript·html
Backstroke fish29 分钟前
Token刷新机制
前端·javascript·vue.js·typescript·vue
小五Five31 分钟前
TypeScript项目中Axios的封装
开发语言·前端·javascript
小曲程序31 分钟前
vue3 封装request请求
java·前端·typescript·vue
临枫54131 分钟前
Nuxt3封装网络请求 useFetch & $fetch
前端·javascript·vue.js·typescript
酷酷的威朗普32 分钟前
医院绩效考核系统
javascript·css·vue.js·typescript·node.js·echarts·html5
前端每日三省32 分钟前
面试题-TS(八):什么是装饰器(decorators)?如何在 TypeScript 中使用它们?
开发语言·前端·javascript
小刺猬_98533 分钟前
(超详细)数组方法 ——— splice( )
前端·javascript·typescript