Vue学习:10.v标签综合-进阶版

再来一节v标签综合...

实例:水果购物车

实现功能:

  1. 显示水果列表:展示可供选择的水果列表,包括名称、价格等信息。

  2. 修改水果数量:允许用户在购物车中增加或减少水果的数量。

  3. 删除水果:允许用户从购物车中移除不需要的水果。

  4. 选择水果进行结算:在用户点击或选择水果时,将其添加到购物车中。

  5. 全选或反选水果:提供全选或反选功能。

  6. 处理结算:提供结算功能,允许用户确认选中的商品并进行支付。

  7. 数据持久化:将购物车中的数据保存在本地存储,以便用户下次访问时恢复数据。

思路:

仅介绍下功能5、6、7的思路。

功能5:首先“全选”功能选择使用复选框标签,并且复选框的状态不是由自己决定的,而是取决于水果列表中每一项的选中状态。所以可以使用v-model绑定复选框的checked,然后使用计算属性动态修改其值。

既然该功能将复选框与水果列表中每一项的选中状态进行的双向绑定,因此既要根据list中每一项的状态来决定checkAll的状态:

get(){
       return this.list.every(item => item.isCheacked)
}

又要将checkAll的状态传递给list中的每一项:

set(value){
           this.list.forEach(item => item.isCheacked = value);
}

功能6:这个功能不同于之前的统计所有水果而是仅统计选中水果的信息,因此要在统计方法中先判断是否被选中,如果选中则计算;否则不计算。

totalPrice(){
             return this.list.reduce((sum,item) => {
                if(item.isCheacked){
                   return sum + item.price*item.numb
                }else{
                 return sum
                }
}, 0)

 功能7:要实现数据的保存,首先要监听数据的变化,因此使用watch。并且要深度监听嵌套对象的变化。然后使用handler回调函数里的localStorage.setItem() 方法将 list 中的新数据转换为 JSON 字符串并保存到本地存储中,以便在页面刷新或重新加载后仍然保持数据。

watch:{
       // 监视数据动态变化,并且保存到当地
       list:{
          deep:true,
          handler(newValue){
              localStorage.setItem('list',JSON.stringify(newValue))
          }
        }
}

同时,vue实例中的数据中的lsit也要与本地存储数据相关联,并且设置本地数组为空时的默认数据。

data: {
       // 将list与本地存储数据相关联,并且设置本地数组为空时的默认数据
       list: JSON.parse(localStorage.getItem('list')) || defaltList,
}

代码:

html:

<div id="app">
        <h3>水果购物车</h3>
        <table v-if="list.length > 0">
            <header>
                <tr>
                    <th>选中</th>
                    <th>水果</th>
                    <th>单价</th>
                    <th>个数</th>
                    <th>小计</th>
                    <th>操作</th>
                </tr>
            </header>
            <tbody>
                <tr v-for="(item, index) in list" :key="item.id" :class="{active:item.isCheacked}">
                    <td><input v-model="item.isCheacked" type="checkbox"></td>
                    <td>{{ item.name }}</td>
                    <td>{{ item.price }}</td>
                    <td>
                        <button :disabled="item.numb <= 1" @click="sub(item.id)">-</button>
                        <span>{{ item.numb }}</span>
                        <button @click="add(item.id)">+</button>
                    </td>
                    <td>{{ item.price*item.numb }}</td>
                    <td>
                        <button @click="del(item.id)" style="width: 60px;background-color: red;">删除</button>
                    </td>
                </tr>
            </tbody>
            <footer>
                <tr>
                    <td colspan="4" style="text-align: left;">
                        <input v-model="checkAll" type="checkbox">全选
                    </td>
                    <td colspan="2">
                        总价:¥{{ totalPrice }}
                        <span style="width: 80px; border: 1px solid black; background-color: #abc;">结算({{ totalNumb }})</span>
                    </td>
                </tr>
            </footer>
        </table>
        <div v-else class="empty">空空如也</div>
</div>

js:

<script>
        // 设置默认数组,当清空缓存后依旧有值(默认情况应为空)
        const defaltList = [
                    {id:1, isCheacked:true, name:'apple', price:2, numb:1},
                    {id:2, isCheacked:false, name:'banana', price:2.5, numb:1},
                    {id:3, isCheacked:false, name:'origan', price:3, numb:10},
                ]
        const app = new Vue({
            el: '#app',
            data: {
                // 将list与本地存储数据相关联,并且设置本地数组为空时的默认数据
                list: JSON.parse(localStorage.getItem('list')) || defaltList,
            },
            computed:{
                // 动态修改其值,并且要与list中的响应,故采用完整写法
                checkAll: {
                    //根据list中每一项的状态来决定checkAll的状态
                    get(){
                        return this.list.every(item => item.isCheacked)
                    },
                    // 将checkAll的状态传递给list中的每一项
                    set(value){
                        this.list.forEach(item => item.isCheacked = value);
                    }
                },
                totalPrice(){
                    return this.list.reduce((sum,item) => {
                        if(item.isCheacked){
                            return sum + item.price*item.numb
                        }else{
                            return sum
                        }
                    }, 0)
                },
                totalNumb(){
                    return this.list.reduce((sum,item) => {
                        if(item.isCheacked){
                            return sum + item.numb
                        }else{
                            return sum
                        }
                    }, 0)
                },
            },
            methods:{
                add(id){
                    let fruit = this.list.find(item => item.id === id)
                    fruit.numb++
                },
                sub(id){
                    let fruit = this.list.find(item => item.id === id)
                    fruit.numb--
                },
                del(id){
                    this.list = this.list.filter(item => item.id != id)
                },
            },
            watch:{
                // 监视数据动态变化,并且保存到当地
                list:{
                    deep:true,
                    handler(newValue){
                        localStorage.setItem('list',JSON.stringify(newValue))
                    }
                }
            }
        })
</script>

css:

<style>
        *{
            box-sizing: border-box;
        }
        table{
            height: 400px;
            width: 600px;
            border-collapse: collapse;
        }
        th,td{
            text-align: center;
            border: 1px solid black;
        }
        button{
            width: 30px;
            height: 30px;
        }
        span{
            width: 30px;
            height: 30px;
            text-align: center;
            line-height: 30px;
        }
        .empty{
            width: 600px;
            height: 200px;
            margin-top: 5px;
            text-align: center;
            vertical-align: bottom;
            border: 1px solid rgb(41, 41, 41);
        }
        .active{
            background-color: #dad7d7;
        }
</style>

posted @ 2024-04-09 15:18  Fly宇航员  阅读(29)  评论(0)    收藏  举报  来源