九、1~8文章的阶段案例

一、案例

现在我们来做一个相对综合一点的练习:书籍购物车

案例说明: 

  • 1.在界面上以表格的形式,显示一些书籍的数据;
  • 2.在底部显示书籍的总价格;
  • 3.点击+或者-可以增加或减少书籍数量(如果为1,那么不能继续-);
  • 4.点击移除按钮,可以将书籍移除(当所有的书籍移除完毕时,显示:购物车为空~);

我们看下下面代码 目前我们只是实现了 1 2 部分。

<!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" />
    <title>Document</title>
    <style>
      table {
        border-collapse: collapse;
        text-align: center;
      }
      thead {
        background-color: #f5f5f5;
      }
      th,
      td {
        border: 1px solid #aaa;
        padding: 8px 16px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 1. 搭建界面内容 -->
      <table>
        <thead>
          <tr>
            <th>序号</th>
            <th>书籍名称</th>
            <th>出版日期</th>
            <th>价格</th>
            <th>购买数量</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <!-- 如果key绑定的值是一个变量,需要使用冒号来进行绑定, 否则Vue会将该值作为字符串进行处理,从而无法实现正确的渲染和响应式。 -->
          <tr v-for="(item, index) in books" :key="item.id">
            <td>{{ index + 1 }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.date }}</td>
            <td>{{ formatPrice(item.price) }}</td>
            <td>
              <button @click="decrement">-</button>
              {{ item.count }}
              <button @click="increment">+</button>
            </td>
            <td><button>移除</button></td>
          </tr>
        </tbody>
      </table>
      <h2>总价: {{ formatPrice(totalPrice) }}</h2>
    </div>
    <script src="../lib/vue.js"></script>
    <script src="./data/data.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            books: books,
          }
        },
        computed: {
          totalPrice() {
            // 1. 直接遍历books
            // let price = 0
            // for (const item of this.books) {
            //   price += item.price * item.count
            // }
            // return price

            // 2. reduce(后面再用这种方法)
            return this.books.reduce((preValue, item) => {
              return preValue + item.price * item.count
            }, 0)
          },
        },
        methods: {
          formatPrice(price) {
            return "¥" + price
          },
          decrement() {
            console.log("点击-")
          },
          increment() {
            console.log("点击+")
          },
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

(1) 问题1:我每一行都有购买数量的加减号,当我点加减号按钮的时候,我怎么获取我想要加的具体是哪一本书呢?我点击移除我怎么区分出来我想移除的是哪一本书呢?

 

思路:我只要能拿到我点击的这本书的索引我就能区分。

那我们看下数据books的结构。 数组里面嵌套对象,所以我们在点击加减号的时候,我们把索引传入到那个函数中,那函数是不是知道是书的id了?我们通过这个索引去books中获取。

this.books[index].count--

下面附源码

<!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" />
    <title>Document</title>
    <style>
      table {
        border-collapse: collapse;
        text-align: center;
      }
      thead {
        background-color: #f5f5f5;
      }
      th,
      td {
        border: 1px solid #aaa;
        padding: 8px 16px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 1. 搭建界面内容 -->
      <table>
        <thead>
          <tr>
            <th>序号</th>
            <th>书籍名称</th>
            <th>出版日期</th>
            <th>价格</th>
            <th>购买数量</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <!-- 如果key绑定的值是一个变量,需要使用冒号来进行绑定, 否则Vue会将该值作为字符串进行处理,从而无法实现正确的渲染和响应式。 -->
          <tr v-for="(item, index) in books" :key="item.id">
            <td>{{ index + 1 }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.date }}</td>
            <td>{{ formatPrice(item.price) }}</td>
            <td>
              <button @click="decrement(index)">-</button>
              {{ item.count }}
              <button @click="increment(item)">+</button>
            </td>
            <td><button >移除</button></td>
          </tr>
        </tbody>
      </table>
      <h2>总价: {{ formatPrice(totalPrice) }}</h2>
    </div>
    <script src="../lib/vue.js"></script>
    <script src="./data/data.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            books: books,
          }
        },
        computed: {
          // 计算属性会监听依赖的响应式数据,如果依赖的数据发生变化,他也会重新计算。
          totalPrice() {
            // 1. 直接遍历books
            // let price = 0
            // for (const item of this.books) {
            //   price += item.price * item.count
            // }
            // return price

            // 2. reduce(后面再用这种方法)
            return this.books.reduce((preValue, item) => {
              return preValue + item.price * item.count
            }, 0)
          },
        },
        methods: {
          formatPrice(price) {
            return "¥" + price
          },
          decrement(index) {
            this.books[index].count--
          },
          // 也可以传item,直接把整条数据拿过来
          increment(item) {
            console.log("点击+")
            item.count++
          },
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

(2)问题2:当数量为1的时候,减号按钮➖置灰

之前我是想过button按钮的置灰不置灰是有一个display属性,我将他的属性值通过data里面一个displayed属性去绑定,发现不能这么玩。因为我改变了data里面的displayed之后,界面所有的减号全部变成了置灰,因为我绑定的是data里面的displayed。

这让我考虑到不能绑定同一个值,所以我想办法让他各自绑定各自的。

思路来了,那我该怎么绑定呢?我可以根据每一行数据的count值来判断他要不要置灰不是吗?

<!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" />
    <title>Document</title>
    <style>
      table {
        border-collapse: collapse;
        text-align: center;
      }
      thead {
        background-color: #f5f5f5;
      }
      th,
      td {
        border: 1px solid #aaa;
        padding: 8px 16px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 1. 搭建界面内容 -->
      <table>
        <thead>
          <tr>
            <th>序号</th>
            <th>书籍名称</th>
            <th>出版日期</th>
            <th>价格</th>
            <th>购买数量</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <!-- 如果key绑定的值是一个变量,需要使用冒号来进行绑定, 否则Vue会将该值作为字符串进行处理,从而无法实现正确的渲染和响应式。 -->
          <tr v-for="(item, index) in books" :key="item.id">
            <td>{{ index + 1 }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.date }}</td>
            <td>{{ formatPrice(item.price) }}</td>
            <td>
              <button @click="decrement(index)" :disabled="item.count <= 1">-</button>
              {{ item.count }}
              <button @click="increment(item)">+</button>
            </td>
            <td><button >移除</button></td>
          </tr>
        </tbody>
      </table>
      <h2>总价: {{ formatPrice(totalPrice) }}</h2>
    </div>
    <script src="../lib/vue.js"></script>
    <script src="./data/data.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            books: books,
          }
        },
        computed: {
          // 计算属性会监听依赖的响应式数据,如果依赖的数据发生变化,他也会重新计算。
          totalPrice() {
            // 1. 直接遍历books
            // let price = 0
            // for (const item of this.books) {
            //   price += item.price * item.count
            // }
            // return price

            // 2. reduce(后面再用这种方法)
            return this.books.reduce((preValue, item) => {
              return preValue + item.price * item.count
            }, 0)
          },
        },
        methods: {
          formatPrice(price) {
            return "¥" + price
          },
          decrement(index) {
            this.books[index].count--
          },
          // 也可以传item,直接把整条数据拿过来
          increment(item) {
            console.log("点击+")
            item.count++
          },
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

(3)移除操作

使用array.splice(index, 1)去删除数组里面的元素,index 是要删除元素的下标,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" />
    <title>Document</title>
    <style>
      table {
        border-collapse: collapse;
        text-align: center;
      }
      thead {
        background-color: #f5f5f5;
      }
      th,
      td {
        border: 1px solid #aaa;
        padding: 8px 16px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 1. 搭建界面内容 -->
      <table>
        <thead>
          <tr>
            <th>序号</th>
            <th>书籍名称</th>
            <th>出版日期</th>
            <th>价格</th>
            <th>购买数量</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody>
          <!-- 如果key绑定的值是一个变量,需要使用冒号来进行绑定, 否则Vue会将该值作为字符串进行处理,从而无法实现正确的渲染和响应式。 -->
          <tr v-for="(item, index) in books" :key="item.id">
            <td>{{ index + 1 }}</td>
            <td>{{ item.name }}</td>
            <td>{{ item.date }}</td>
            <td>{{ formatPrice(item.price) }}</td>
            <td>
              <button @click="decrement(index)" :disabled="item.count <= 1">-</button>
              {{ item.count }}
              <button @click="increment(item)">+</button>
            </td>
            <td><button @click="removeBook(index)">移除</button></td>
          </tr>
        </tbody>
      </table>
      <h2>总价: {{ formatPrice(totalPrice) }}</h2>
    </div>
    <script src="../lib/vue.js"></script>
    <script src="./data/data.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            books: books,
          }
        },
        computed: {
          // 计算属性会监听依赖的响应式数据,如果依赖的数据发生变化,他也会重新计算。
          totalPrice() {
            // 1. 直接遍历books
            // let price = 0
            // for (const item of this.books) {
            //   price += item.price * item.count
            // }
            // return price

            // 2. reduce(后面再用这种方法)
            return this.books.reduce((preValue, item) => {
              return preValue + item.price * item.count
            }, 0)
          },
        },
        methods: {
          formatPrice(price) {
            return "¥" + price
          },
          decrement(index) {
            this.books[index].count--
          },
          // 也可以传item,直接把整条数据拿过来
          increment(item) {
            console.log("点击+")
            item.count++
          },
          removeBook(index) {
            this.books.splice(index, 1)
          },
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

(4)删到最后一个,显示”购物车没有已选择的商品啦“

<!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" />
    <title>Document</title>
    <style>
      table {
        border-collapse: collapse;
        text-align: center;
      }
      thead {
        background-color: #f5f5f5;
      }
      th,
      td {
        border: 1px solid #aaa;
        padding: 8px 16px;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 1. 搭建界面内容 -->
      <template v-if="books.length">
        <table>
          <thead>
            <tr>
              <th>序号</th>
              <th>书籍名称</th>
              <th>出版日期</th>
              <th>价格</th>
              <th>购买数量</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <!-- 如果key绑定的值是一个变量,需要使用冒号来进行绑定, 否则Vue会将该值作为字符串进行处理,从而无法实现正确的渲染和响应式。 -->
            <tr v-for="(item, index) in books" :key="item.id">
              <td>{{ index + 1 }}</td>
              <td>{{ item.name }}</td>
              <td>{{ item.date }}</td>
              <td>{{ formatPrice(item.price) }}</td>
              <td>
                <button @click="decrement(index)" :disabled="item.count <= 1">-</button>
                {{ item.count }}
                <button @click="increment(item)">+</button>
              </td>
              <td><button @click="removeBook(index)">移除</button></td>
            </tr>
          </tbody>
        </table>
        <h2>总价: {{ formatPrice(totalPrice) }}</h2>
      </template>

      <template v-else>
        <h1>购物车为空,请添加喜欢的书籍~</h1>
      </template>
    </div>
    <script src="../lib/vue.js"></script>
    <script src="./data/data.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            books: books,
          }
        },
        computed: {
          // 计算属性会监听依赖的响应式数据,如果依赖的数据发生变化,他也会重新计算。
          totalPrice() {
            // 1. 直接遍历books
            // let price = 0
            // for (const item of this.books) {
            //   price += item.price * item.count
            // }
            // return price

            // 2. reduce(后面再用这种方法)
            return this.books.reduce((preValue, item) => {
              return preValue + item.price * item.count
            }, 0)
          },
        },
        methods: {
          formatPrice(price) {
            return "¥" + price
          },
          decrement(index) {
            this.books[index].count--
          },
          // 也可以传item,直接把整条数据拿过来
          increment(item) {
            console.log("点击+")
            item.count++
          },
          removeBook(index) {
            this.books.splice(index, 1)
          },
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

(5)有一些数据,实现功能:点击A选中A,点击B选中B,A变成未选中状态。

<!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" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <ul>
        <li v-for="item in movies">{{ item }}</li>
      </ul>
    </div>
    <script src="../lib/vue.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            movies: ["星际穿越", "阿凡达", "大话西游", "黑客帝国"],
          }
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

 

思路:控制active放在哪个元素上面。

 我直接这样子写的话 每个元素都会被active

 

我们先来一个简单的需求,将索引值为1的li添加上active 

<!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" />
    <title>Document</title>
    <style>
      .active {
        color: red;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <ul>
        <!-- <h2 :class="{title: true}"></h2> -->

        <!-- 对active的class进行动态绑定 -->
        <!-- 需求:将索引值为1的li添加上active -->
        <li :class="{active: index === 1}" v-for="(item, index) in movies">{{ item }}</li>
      </ul>
    </div>
    <script src="../lib/vue.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            movies: ["星际穿越", "阿凡达", "大话西游", "黑客帝国"],
          }
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

功能是不是实现了?哈哈 

 


 那怎么 选中哪就给哪一个添加active呢

思路:用一个变量(属性)记录当前点击的位置

<!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" />
    <title>Document</title>
    <style>
      .active {
        color: red;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <ul>
        <!-- <h2 :class="{title: true}"></h2> -->

        <!-- 对active的class进行动态绑定 -->
        <!-- 需求1:将索引值为1的li添加上active -->
        <!-- 需求2 选中哪就给哪一个添加active 思路:用一个变量(属性)记录当前点击的位置-->
        <li :class="{active: index === currentIndex}" v-for="(item, index) in movies" @click="liClick(index)">{{ item }}</li>
      </ul>
    </div>
    <script src="../lib/vue.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            movies: ["星际穿越", "阿凡达", "大话西游", "黑客帝国"],
            currentIndex: -1,
          }
        },
        methods: {
          liClick(index) {
            this.currentIndex = index
          },
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

那上面购物车的案例,我们也给他实现这个功能。

<!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" />
    <title>Document</title>
    <style>
      table {
        border-collapse: collapse;
        text-align: center;
      }
      thead {
        background-color: #f5f5f5;
      }
      th,
      td {
        border: 1px solid #aaa;
        padding: 8px 16px;
      }
      .active {
        background: skyblue;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <!-- 1. 搭建界面内容 -->
      <template v-if="books.length">
        <table>
          <thead>
            <tr>
              <th>序号</th>
              <th>书籍名称</th>
              <th>出版日期</th>
              <th>价格</th>
              <th>购买数量</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            <!-- 如果key绑定的值是一个变量,需要使用冒号来进行绑定, 否则Vue会将该值作为字符串进行处理,从而无法实现正确的渲染和响应式。 -->
            <tr v-for="(item, index) in books" :key="item.id" @click="rowClick(index)" :class="{ active: index === currentIndex }">
              <td>{{ index + 1 }}</td>
              <td>{{ item.name }}</td>
              <td>{{ item.date }}</td>
              <td>{{ formatPrice(item.price) }}</td>
              <td>
                <button @click="decrement(index)" :disabled="item.count <= 1">-</button>
                {{ item.count }}
                <button @click="increment(item)">+</button>
              </td>
              <td><button @click="removeBook(index)">移除</button></td>
            </tr>
          </tbody>
        </table>
        <h2>总价: {{ formatPrice(totalPrice) }}</h2>
      </template>

      <template v-else>
        <h1>购物车为空,请添加喜欢的书籍~</h1>
      </template>
    </div>
    <script src="../lib/vue.js"></script>
    <script src="./data/data.js"></script>

    <script>
      const app = Vue.createApp({
        // data: option api
        data() {
          return {
            books: books,
            currentIndex: 0,
          }
        },
        computed: {
          // 计算属性会监听依赖的响应式数据,如果依赖的数据发生变化,他也会重新计算。
          totalPrice() {
            // 1. 直接遍历books
            // let price = 0
            // for (const item of this.books) {
            //   price += item.price * item.count
            // }
            // return price

            // 2. reduce(后面再用这种方法)
            return this.books.reduce((preValue, item) => {
              return preValue + item.price * item.count
            }, 0)
          },
        },
        methods: {
          formatPrice(price) {
            return "¥" + price
          },
          decrement(index) {
            this.books[index].count--
          },
          // 也可以传item,直接把整条数据拿过来
          increment(item) {
            console.log("点击+")
            item.count++
          },
          removeBook(index) {
            this.books.splice(index, 1)
          },
          rowClick(index) {
            this.currentIndex = index
          },
        },
      })

      app.mount("#app")
    </script>
  </body>
</html>

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值