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>
相关推荐
kyriewen4 分钟前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
蜗牛前端32 分钟前
codex 全流程开发上线的高颜值礼簿小程序
前端·微信小程序
大龄秃头程序员1 小时前
我在图文流 App 里落地双层缓存、弱网降级与 OOM 治理
前端
老王以为1 小时前
React Renderer 分离的多平台架构
前端·react native·react.js
hunterandroid1 小时前
Kotlin Coroutines 与 Flow:让异步任务更清晰
前端
Bigger2 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
lichenyang4532 小时前
JSAPI、NAPI、Biz、Imp:ASCF Demo 如何真正调用系统能力和 C++ 能力
前端
lichenyang4532 小时前
IPC、JSVM、UIThread、libuv:ASCF 架构图里最容易混的几个词
前端
用户059540174463 小时前
Redis记忆存储故障恢复测试踩坑实录:手动测试让我漏掉了2个一致性Bug
前端·css
用户2136610035723 小时前
Vue2脚手架工程化与Axios集成
前端·vue.js