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

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

相关推荐
一 乐1 小时前
学籍管理平台|在线学籍管理平台系统|基于Springboot+VUE的在线学籍管理平台系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·学习
小御姐@stella1 小时前
Vue 之组件插槽Slot用法(组件间通信一种方式)
前端·javascript·vue.js
加油,旭杏2 小时前
【中间件学习】fastCG介绍和使用
学习·nginx·fastcgi
limengshi1383922 小时前
通信工程学习:什么是TFTP简单文件传输协议
网络·网络协议·学习·信息与通信
GFCGUO2 小时前
ubuntu18.04运行OpenPCDet出现的问题
linux·python·学习·ubuntu·conda·pip
丝丝不是土豆丝4 小时前
学习 CSS 新的属性 conic-gradient 实现环形进度条
学习
S hh4 小时前
【Linux】进程地址空间
java·linux·运维·服务器·学习
万叶学编程4 小时前
Day02-JavaScript-Vue
前端·javascript·vue.js
wusam5 小时前
螺蛳壳里做道场:老破机搭建的私人数据中心---Centos下Docker学习04(环境准备)
学习·docker·centos
攸攸太上5 小时前
Spring Gateway学习
java·后端·学习·spring·微服务·gateway