vue学习日记10:综合案例-购物车

一、需求说明

1.渲染功能

(1)代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="./css/inputnumber.css" />
  <link rel="stylesheet" href="./css/index.css" />
  <title>购物车</title>
</head>
<body>
<div class="app-container" id="app">
  <!-- 顶部banner -->
  <div class="banner-box"><img src="img/fruit.jpg" 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 v-for="(item, index) in fruitList" :key="item.id" class="tr" :class="{ active: item.isChecked }">
          <div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
          <div class="td"><img :src="item.icon" alt="" /></div>
          <div class="td">{{ item.price }}</div>
          <div class="td">
            <div class="my-input-number">
              <button class="decrease"> - </button>
              <span class="my-input__inner">{{ item.num }}</span>
              <button class="increase"> + </button>
            </div>
          </div>
          <div class="td">{{ item.num * item.price }}</div>
          <div class="td"><button>删除</button></div>
        </div>
      </div>
    </div>
    <!-- 底部 -->
    <div class="bottom">
      <!-- 全选 -->
      <label class="check-all">
        <input type="checkbox" />
        全选
      </label>
      <div class="right-box">
        <!-- 所有商品总价 -->
        <span class="price-box">总价&nbsp;&nbsp;:&nbsp;&nbsp;¥&nbsp;<span class="price">24</span></span>
        <!-- 结算按钮 -->
        <button class="pay">结算( 6 )</button>
      </div>
    </div>
  </div>
  <!-- 空车 -->
  <div class="empty" v-else>🛒空空如也</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      // 水果列表
      fruitList: [
        {
          id: 1,
          icon: 'img/火龙果.png',
          isChecked: true,
          num: 2,
          price: 6,
        },
        {
          id: 2,
          icon: 'img/荔枝.png',
          isChecked: false,
          num: 7,
          price: 20,
        },
        {
          id: 3,
          icon: 'img/榴莲.png',
          isChecked: false,
          num: 3,
          price: 40,
        },
        {
          id: 4,
          icon: 'img/鸭梨.png',
          isChecked: true,
          num: 10,
          price: 3,
        },
        {
          id: 5,
          icon: 'img/樱桃.png',
          isChecked: false,
          num: 20,
          price: 34,
        },
      ],
    },
  })
</script>
</body>
</html>

(2)展示

2.删除和修改数量

(1)代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="./css/inputnumber.css" />
  <link rel="stylesheet" href="./css/index.css" />
  <title>购物车</title>
</head>
<body>
<div class="app-container" id="app">
  <!-- 顶部banner -->
  <div class="banner-box"><img src="img/fruit.jpg" 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 v-for="(item, index) in fruitList" :key="item.id" class="tr" :class="{ active: item.isChecked }">
          <div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
          <div class="td"><img :src="item.icon" alt="" /></div>
          <div class="td">{{ item.price }}</div>
          <div class="td">
            <div class="my-input-number">
              <button :disabled="item.num <=1" 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.num * item.price }}</div>
          <div class="td"><button @click="del(item.id)">删除</button></div>
        </div>
      </div>
    </div>
    <!-- 底部 -->
    <div class="bottom">
      <!-- 全选 -->
      <label class="check-all">
        <input type="checkbox" />
        全选
      </label>
      <div class="right-box">
        <!-- 所有商品总价 -->
        <span class="price-box">总价&nbsp;&nbsp;:&nbsp;&nbsp;¥&nbsp;<span class="price">24</span></span>
        <!-- 结算按钮 -->
        <button class="pay">结算( 6 )</button>
      </div>
    </div>
  </div>
  <!-- 空车 -->
  <div class="empty" v-else>🛒空空如也</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      // 水果列表
      fruitList: [
        {
          id: 1,
          icon: 'img/火龙果.png',
          isChecked: true,
          num: 2,
          price: 6,
        },
        {
          id: 2,
          icon: 'img/荔枝.png',
          isChecked: false,
          num: 7,
          price: 20,
        },
        {
          id: 3,
          icon: 'img/榴莲.png',
          isChecked: false,
          num: 3,
          price: 40,
        },
        {
          id: 4,
          icon: 'img/鸭梨.png',
          isChecked: true,
          num: 10,
          price: 3,
        },
        {
          id: 5,
          icon: 'img/樱桃.png',
          isChecked: false,
          num: 20,
          price: 34,
        },
      ],
    },
    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++
        // console.log(id)
      },
      sub(id){
        //1.根据id 找到数组中的对应项 -> find
        const fruit = this.fruitList.find(item => item.id === id)
        //2.操作num数量
        fruit.num--
        // console.log(id)


      }
    },
  })
</script>
</body>
</html>

(2)展示

减到1就不能再减,点击删除直接删除

3.全选与反选

(1)代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="./css/inputnumber.css" />
  <link rel="stylesheet" href="./css/index.css" />
  <title>购物车</title>
</head>
<body>
<div class="app-container" id="app">
  <!-- 顶部banner -->
  <div class="banner-box"><img src="img/fruit.jpg" 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 v-for="(item, index) in fruitList" :key="item.id" class="tr" :class="{ active: item.isChecked }">
          <div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
          <div class="td"><img :src="item.icon" alt="" /></div>
          <div class="td">{{ item.price }}</div>
          <div class="td">
            <div class="my-input-number">
              <button :disabled="item.num <=1" 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.num * item.price }}</div>
          <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">24</span></span>
        <!-- 结算按钮 -->
        <button class="pay">结算( 6 )</button>
      </div>
    </div>
  </div>
  <!-- 空车 -->
  <div class="empty" v-else>🛒空空如也</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      // 水果列表
      fruitList: [
        {
          id: 1,
          icon: 'img/火龙果.png',
          isChecked: true,
          num: 2,
          price: 6,
        },
        {
          id: 2,
          icon: 'img/荔枝.png',
          isChecked: false,
          num: 7,
          price: 20,
        },
        {
          id: 3,
          icon: 'img/榴莲.png',
          isChecked: false,
          num: 3,
          price: 40,
        },
        {
          id: 4,
          icon: 'img/鸭梨.png',
          isChecked: true,
          num: 10,
          price: 3,
        },
        {
          id: 5,
          icon: 'img/樱桃.png',
          isChecked: false,
          num: 20,
          price: 34,
        },
      ],
    },
    computed:{
      //默认计算属性,只能能获取不能设置,要设置需要写完整写法
      // isAll(){
      //   //必须所有的小选框都选中,全选按钮才选中 ------>every
      //   return this.fruitList.every(item => item.isChecked)
      // },

      // 完整写法 => get+set
      isAll:{
        get(){
          return this.fruitList.every(item => item.isChecked)
        },
        set(value){
          //基于拿到的布尔值,要让所有的小选框 同步状态
          this.fruitList.forEach(item => item.isChecked = value)
          // console.log(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++
        // console.log(id)
      },
      sub(id){
        //1.根据id 找到数组中的对应项 -> find
        const fruit = this.fruitList.find(item => item.id === id)
        //2.操作num数量
        fruit.num--
        // console.log(id)


      }
    },
  })
</script>
</body>
</html>

(2)展示

4.统计选中的总价和总数量

(1)代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="./css/inputnumber.css" />
  <link rel="stylesheet" href="./css/index.css" />
  <title>购物车</title>
</head>
<body>
<div class="app-container" id="app">
  <!-- 顶部banner -->
  <div class="banner-box"><img src="img/fruit.jpg" 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 v-for="(item, index) in fruitList" :key="item.id" class="tr" :class="{ active: item.isChecked }">
          <div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
          <div class="td"><img :src="item.icon" alt="" /></div>
          <div class="td">{{ item.price }}</div>
          <div class="td">
            <div class="my-input-number">
              <button :disabled="item.num <=1" 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.num * item.price }}</div>
          <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">结算{{ totalCount }}</button>
      </div>
    </div>
  </div>
  <!-- 空车 -->
  <div class="empty" v-else>🛒空空如也</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
  const app = new Vue({
    el: '#app',
    data: {
      // 响应式数据
      // 水果列表
      fruitList: [
        {
          id: 1,
          icon: 'img/火龙果.png',
          isChecked: true,
          num: 2,
          price: 6,
        },
        {
          id: 2,
          icon: 'img/荔枝.png',
          isChecked: false,
          num: 7,
          price: 20,
        },
        {
          id: 3,
          icon: 'img/榴莲.png',
          isChecked: false,
          num: 3,
          price: 40,
        },
        {
          id: 4,
          icon: 'img/鸭梨.png',
          isChecked: true,
          num: 10,
          price: 3,
        },
        {
          id: 5,
          icon: 'img/樱桃.png',
          isChecked: false,
          num: 20,
          price: 34,
        },
      ],

    },
    computed:{
      // 计算属性

      //默认计算属性,只能能获取不能设置,要设置需要写完整写法
      // isAll(){
      //   //必须所有的小选框都选中,全选按钮才选中 ------>every
      //   return this.fruitList.every(item => item.isChecked)
      // },

      // 完整写法 => get+set
      isAll:{
        get(){
          return this.fruitList.every(item => item.isChecked)
        },
        set(value){
          //基于拿到的布尔值,要让所有的小选框 同步状态
          this.fruitList.forEach(item => item.isChecked = value)
          // console.log(value)
        },
      },
      // 统计选中的总数
      totalCount(){
        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)

      },

    },
    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++
        // console.log(id)
      },
      sub(id){
        //1.根据id 找到数组中的对应项 -> find
        const fruit = this.fruitList.find(item => item.id === id)
        //2.操作num数量
        fruit.num--
        // console.log(id)


      },

    },

  })
</script>
</body>
</html>

这里写三元表达式也可以

三元表达式是一种在许多编程语言中常见的条件表达式,它由三个操作数组成:一个条件表达式、一个真值表达式和一个假值表达式。其一般形式为:

condition ? expr1 : expr2

如果写成if...else

if (condition) {
  result = expr1;
} else {
  result = expr2;
}

三元写法

result = condition ? expr1 : expr2;

(2)展示

被勾选的被计算

5.持久化本地

如果用户对其操作之后 没有持久化刷新后就会成问题

(1)代码

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="./css/inputnumber.css" />
  <link rel="stylesheet" href="./css/index.css" />
  <title>购物车</title>
</head>
<body>
<div class="app-container" id="app">
  <!-- 顶部banner -->
  <div class="banner-box"><img src="img/fruit.jpg" 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 v-for="(item, index) in fruitList" :key="item.id" class="tr" :class="{ active: item.isChecked }">
          <div class="td"><input type="checkbox" v-model="item.isChecked" /></div>
          <div class="td"><img :src="item.icon" alt="" /></div>
          <div class="td">{{ item.price }}</div>
          <div class="td">
            <div class="my-input-number">
              <button :disabled="item.num <=1" 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.num * item.price }}</div>
          <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">结算{{ totalCount }}</button>
      </div>
    </div>
  </div>
  <!-- 空车 -->
  <div class="empty" v-else>🛒空空如也</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
  const defaultArr = [
    {
      id: 1,
      icon: 'img/火龙果.png',
      isChecked: false,
      num: 1,
      price: 6,
    },
    {
      id: 2,
      icon: 'img/荔枝.png',
      isChecked: false,
      num: 1,
      price: 20,
    },
    {
      id: 3,
      icon: 'img/榴莲.png',
      isChecked: false,
      num: 1,
      price: 40,
    },
    {
      id: 4,
      icon: 'img/鸭梨.png',
      isChecked: false,
      num: 1,
      price: 3,
    },
    {
      id: 5,
      icon: 'img/樱桃.png',
      isChecked: false,
      num: 1,
      price: 34,
    },
  ]
  const app = new Vue({
    el: '#app',
    data: {
      // 响应式数据
      // 水果列表
      fruitList: JSON.parse(localStorage.getItem('list')) || (defaultArr),

    },
    computed:{
      // 计算属性

      //默认计算属性,只能能获取不能设置,要设置需要写完整写法
      // isAll(){
      //   //必须所有的小选框都选中,全选按钮才选中 ------>every
      //   return this.fruitList.every(item => item.isChecked)
      // },

      // 完整写法 => get+set
      isAll:{
        get(){
          return this.fruitList.every(item => item.isChecked)
        },
        set(value){
          //基于拿到的布尔值,要让所有的小选框 同步状态
          this.fruitList.forEach(item => item.isChecked = value)
          // console.log(value)
        },
      },
      // // 统计选中的总数
      // totalCount(){
      //   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)
      // },

      //三元表达式
      // 统计选中的总数
      totalCount() {
        return this.fruitList.reduce((sum, item) => {
          return item.isChecked ? sum + item.num : sum;
        }, 0);
      },

      // 统计选中的总价
      totalPrice() {
        return this.fruitList.reduce((sum, item) => {
          return item.isChecked ? sum + item.num * item.price : sum;
        }, 0);
      },


    },
    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++
        // console.log(id)
      },
      sub(id){
        //1.根据id 找到数组中的对应项 -> find
        const fruit = this.fruitList.find(item => item.id === id)
        //2.操作num数量
        fruit.num--
        // console.log(id)


      },

    },
    watch:{
      fruitList:{
        deep:true,
        handler(newValue){
          // 需要将变化后的newValue 存入本地 (转json)
          localStorage.setItem('list',JSON.stringify(newValue))

        }
      }
    }


  })
</script>
</body>
</html>

如果没有 || (defaultArr)清除缓存后就会null崩溃 ,加上之后就会默认回到初试页面了

(2)展示

6.总结


注:本人是根据黑马程序员的B站教程来学习的,

链接:https://www.bilibili.com/video/BV1HV4y1a7n4/?spm_id_from=333.999.0.0

本文章仅仅是个人学习笔记 无任何其他用途 特此说明

相关推荐
虾球xz3 分钟前
游戏引擎学习第20天
前端·学习·游戏引擎
LateBloomer77712 分钟前
FreeRTOS——信号量
笔记·stm32·学习·freertos
疯狂的沙粒12 分钟前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
legend_jz16 分钟前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
Komorebi.py17 分钟前
【Linux】-学习笔记04
linux·笔记·学习
小镇程序员28 分钟前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
weiabc1 小时前
学习electron
javascript·学习·electron
想自律的露西西★1 小时前
用el-scrollbar实现滚动条,拖动滚动条可以滚动,但是通过鼠标滑轮却无效
前端·javascript·css·vue.js·elementui·前端框架·html5
白墨阳2 小时前
vue3:瀑布流
前端·javascript·vue.js
HackKong2 小时前
小白怎样入门网络安全?
网络·学习·安全·web安全·网络安全·黑客