二六(vue2-02)、指令修饰符、v-bind增强、v-model补充、computed、watch、水果购物车

SpringBoot+Vue+Mysql社区医院信息平台(源码+文档) SpringBoot+Vue+Mysql学生宿舍信息管理系统 WEB端软件项目(源码+文档) 项目包含:①完整源码(后端+管理后台+用户前台);②数据库脚本文件;③万字论文文档;④开题报告;⑤答辩PPT;⑥项目运行视频教程。 项目架构: B/S 架构、MVC 分层设计 技术栈: Java、MySQL、SpringBoot、MyBatis-Plus、Token 权限校验、管理后台 Vue+ElementUI+ECharts、用户前台 Layui+jQuery、Maven 构建。 一、角色设计 学生用户: 注册登录、浏览宿舍信息、查询宿舍安排、在线报修、缴费查询、失物招领、桶装水预订、评论交流、收藏等。 商家用户: 注册登录、桶装水品牌与商品信息维护、预订订单查看与处理。 维修人员: 注册登录、查看待处理报修、接单维修、填写报修处理结果与进度反馈。 管理员: 学生管理、班级管理、宿舍信息管理、宿舍安排管理、报修处理管理、缴费信息管理、卫生检查与文明宿舍管理、失物招领管理、桶装水与商家管理、维修人员管理、公告资讯与轮播图管理等。 二、前台功能模块(学生端) 1. 首页展示:轮播图、宿舍公告、最新动态推荐、常用服务快速入口 2. 宿舍信息模块:宿舍列表、楼栋与房间条件筛选、宿舍详情查看、我的宿舍安排查询 3. 在线报修模块:在线提交宿舍设施报修申请、上传故障描述、查看报修进度与维修处理结果 4. 缴费模块:水电等缴费记录查询、缴费状态查看 5. 卫生检查与文明宿舍:宿舍卫生检查结果公示、文明宿舍展示 6. 失物招领模块:失物信息发布、招领信息发布、详情查看、评论互动 7. 桶装水预订:桶装水品牌与商品浏览、在线预订、我的预订记录查询 8. 假期留宿:假期留校申请提交、申请记录查询 9. 评论与收藏:信息评论、查看他人评论、评论回复互动、收藏感兴趣的内容 10. 个人中心... 立即下载

1. 指令补充

1.1 指令修饰符

1.1.1 按键修饰符

<!-- 指令修饰符: 通过"."指明一些指令后缀,不同后缀封装了不同的处理操作 → 简化代码 -->

<body>
  <div id="app">
    <!-- 1. 按键修饰符 -->
    <h3>@keyup.enter → 监听键盘回车事件</h3>
    <!-- <input v-model="username" type="text"> -->
    <input type="text" @keyup="f1">
    <input type="text" @keyup.enter="f2">
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: ''
      },
      methods: {
        f1(e) {
          if (e.key === 'Enter') {
            console.log('原来,按下回车键')
          }
        },
        f2() {
          console.log('指令修饰符,按下回车')
        }
      }
    })
  </script>
</body>

1.1.2 v-model 修饰符

1.1.3 事件修饰符

<!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>
    .father {
      width: 200px;
      height: 200px;
      background-color: pink;
      margin-top: 20px;
    }

    .son {
      width: 100px;
      height: 100px;
      background-color: skyblue;
    }
  </style>
</head>

<body>
  <div id="app">
    <!-- 2. v-model修饰符 -->
    <h3>v-model修饰符 .trim .number</h3>
    <!-- v-model.trim 去除首尾空格 -->
    姓名:<input v-model.trim="username" type="text"><br>
    <!-- v-model.number 转数字 -->
    年纪:<input v-model.number="age" type="text"><br>

    <!-- 3. 事件修饰符 -->
    <h3>@事件名.stop → 阻止冒泡</h3>
    <div @click="fatherFn" class="father">
      <div @click.stop="sonFn" class="son">儿子</div>
    </div>

    <h3>@事件名.prevent → 阻止默认行为</h3>
    <a @click.prevent href="http://www.baidu.com">阻止默认行为</a>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: '',
        age: '',
      },
      methods: {
        fatherFn() {
          alert('老父亲被点击了')
        },
        sonFn(e) {
          // e.stopPropagation()
          alert('儿子被点击了')
        }
      }
    })
  </script>
</body>

</html>

1.2 v-bind 对于样式操作的增强

1.2.1 操作 class

<!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>
    .box {
      width: 200px;
      height: 200px;
      border: 3px solid #000;
      font-size: 30px;
      margin-top: 10px;
    }

    .pink {
      background-color: pink;
    }

    .big {
      width: 300px;
      height: 300px;
    }
  </style>
</head>

<!-- 
  v-bind 对于样式控制的增强
  为了方便开发者进行样式控制,Vue扩展了v-bind的语法,可以针对class类名和style行内样式进行控制 。 
-->
<!-- 
  1. 操作class
  语法 :class = "对象/数组"
  1.1 对象 → 键就是类名,值是布尔值。如果值为 true,有这个类,否则没有这个类
    <div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值 }"></div>
    适用场景:一个类名,来回切换
  1.2 数组 → 数组中所有的类,都会添加到盒子上,本质就是一个 class 列表
    <div class="box" :class="[ 类名1, 类名2, 类名3 ]"></div>
    适用场景:批量添加或删除类
-->

<body>
  <div id="app">
    <div class="box" :class="{pink:pinkFlag,big:bigFlag}">黑马程序员</div>
    <div class="box" :class="['pink','big']">黑马程序员</div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        pinkFlag: true,
        bigFlag: false
      }
    })
  </script>
</body>

</html>

案例1 - 京东秒杀 tab 导航高亮
<!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>
    * {
      margin: 0;
      padding: 0;
    }

    ul {
      display: flex;
      border-bottom: 2px solid #e01222;
      padding: 0 10px;
    }

    li {
      width: 100px;
      height: 50px;
      line-height: 50px;
      list-style: none;
      text-align: center;
    }

    li a {
      display: block;
      text-decoration: none;
      font-weight: bold;
      color: #333333;
    }

    li a.active {
      background-color: #e01222;
      color: #fff;
    }
  </style>
</head>

<!--
  案例:京东秒杀 tab 导航高亮
  核心思路:
    1. 基于数据动态渲染 tab → v-for
    2. 准备下标记录高亮的是哪一个 tab → activeIndex
    3. 基于下标,动态控制 class 类名 → v-bind:class
-->

<body>
  <div id="app">
    <ul>
      <!-- <li><a class="active" href="#">京东秒杀</a></li>
      <li><a href="#">每日特价</a></li>
      <li><a href="#">品类秒杀</a></li> -->
      <li v-for="(item,index) in list" :key="item.id" @click="num=index">
        <a href="#" :class="{active: num===index}">{{item.name}}</a>
      </li>
    </ul>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        num: 0,
        list: [
          { id: 1, name: '京东秒杀' },
          { id: 2, name: '每日特价' },
          { id: 3, name: '品类秒杀' }
        ]
      }
    })
  </script>
</body>

</html>

1.2.2 操作 style

<!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>
    .box {
      width: 200px;
      height: 200px;
      background-color: rgb(187, 150, 156);
    }
  </style>
</head>

<!-- 
  2. 操作style
  语法 :style = "样式对象"
    <div class="box" :style="{ CSS属性名1: CSS属性值, CSS属性名2: CSS属性值 }"></div>
    适用场景:某个具体属性的动态设置
-->

<body>
  <div id="app">
    <div class="box"></div>
    <!-- 带中横杠的属性,要变小驼峰; 如 background-color → backgroundColor -->
    <div class="box" :style="{width: w,height: h+'px',backgroundColor: myColor,}"></div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        w: '100px',
        h: 100,
        myColor: 'red',
      }
    })
  </script>
</body>

</html>

案例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>
    .progress {
      height: 25px;
      width: 400px;
      border-radius: 15px;
      background-color: #272425;
      border: 3px solid #272425;
      box-sizing: border-box;
      margin-bottom: 30px;
    }

    .inner {
      width: 50%;
      height: 20px;
      border-radius: 10px;
      text-align: right;
      position: relative;
      background-color: #409eff;
      background-size: 20px 20px;
      box-sizing: border-box;
      transition: all 1s;
    }

    .inner span {
      position: absolute;
      right: -20px;
      bottom: -25px;
    }
  </style>
</head>

<body>
  <div id="app">
    <div class="progress">
      <div class="inner" :style="{width:num+'%'}">
        <span>{{num}}%</span>
      </div>
    </div>
    <button @click="num=25">设置25%</button>
    <button @click="num=50">设置50%</button>
    <button @click="num=75">设置75%</button>
    <button @click="num=100">设置100%</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        num: 5,
      }
    })
  </script>
</body>

</html>

1.3 v-model 应用于其他表单元素

<!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>
    textarea {
      display: block;
      width: 240px;
      height: 100px;
      margin: 10px 0;
    }
  </style>
</head>

<!-- 
  v-model 应用于其他表单元素
  v-model → 快速 获取 或 设置 表单元素的值
  v-model 会根据 控件类型 自动选取 正确的方法 来更新元素

  输入框 input:text → value
  文本域 textarea → value
  复选框 input:checkbox → checked
  单选框 input:radio → checked
  下拉菜单 select → value
-->

<body>
  <div id="app">
    <h3>小黑学习网</h3>

    姓名:
    <input type="text" v-model.trim="username">
    <br><br>

    是否单身:
    <input type="checkbox" v-model="isSingle">
    <br><br>

    <!-- 
      前置理解:
        1. name:  给单选框加上 name 属性 可以分组 → 同一组互相会互斥
        2. value: 给单选框加上 value 属性,用于提交给后台的数据
      结合 Vue 使用 → v-model
    -->
    性别:
    <input type="radio" name="gender" value="0" v-model="gender">男
    <input type="radio" name="gender" value="1" v-model="gender">女
    <br><br>

    <!-- 
      前置理解:
        1. option 需要设置 value 值,提交给后台
        2. select 的 value 值,关联了选中的 option 的 value 值
      结合 Vue 使用 → v-model
    -->
    所在城市:
    <select v-model="selCity">
      <option value="bj">北京</option>
      <option value="sh">上海</option>
      <option value="cd">成都</option>
      <option value="nj">南京</option>
    </select>
    <br><br>

    自我描述:
    <textarea></textarea>

    <button>立即注册</button>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        username: '',
        isSingle: true,
        gender: 1,
        selCity: 'sh'
      }
    })
  </script>
</body>

</html>

2. computed 计算属性

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">
  <title>Document</title>
  <style>
    table {
      border: 1px solid #000;
      text-align: center;
      width: 240px;
    }

    th,
    td {
      border: 1px solid #000;
    }

    h3 {
      position: relative;
    }
  </style>
</head>

<!-- 
  计算属性 computed
  概念:基于现有的数据,计算出来的新属性。依赖的数据变化,自动重新计算。
  语法:
    1. 声明在 computed 配置项中,一个计算属性对应一个函数
    2. 使用起来和普通属性一样使用 {{ 计算属性名 }}
    计算属性 → 可以将一段 求值的代码 进行封装
-->

<body>
  <div id="app">
    <h3>小黑的礼物清单</h3>
    <table>
      <tr>
        <th>名字</th>
        <th>数量</th>
      </tr>
      <tr v-for="(item, index) in list" :key="item.id">
        <td>{{ item.name }}</td>
        <td>{{ item.num }}个</td>
      </tr>
    </table>

    <!-- 目标:统计求和,求得礼物总数 reduce? -->
    <p>礼物总数:{{total}} 个</p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        // 现有的数据
        list: [
          { id: 1, name: '篮球', num: 1 },
          { id: 2, name: '玩具', num: 2 },
          { id: 3, name: '铅笔', num: 5 },
        ]
      },
      computed: {
        total() {
          // arr.reduce(function(上一次值,当前值){}, 初始值)
          return this.list.reduce((sum, item) => sum += item.num, 0)
        },
      }
    })
  </script>
</body>

</html>

2.2 计算属性 vs 方法

<!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: 1px solid #000;
      text-align: center;
      width: 300px;
    }

    th,
    td {
      border: 1px solid #000;
    }

    h3 {
      position: relative;
    }

    span {
      position: absolute;
      left: 145px;
      top: -4px;
      width: 16px;
      height: 16px;
      color: white;
      font-size: 12px;
      text-align: center;
      border-radius: 50%;
      background-color: #e63f32;
    }
  </style>
</head>

<!-- 
  computed 计算属性 vs methods 方法
  1. computed 计算属性:
    作用:封装了一段对于数据的处理,求得一个结果。
    作为属性,直接使用 → this.计算属性 {{ 计算属性 }}
  2. methods 方法:
    作用:给实例提供一个方法,调用以处理业务逻辑。
    作为方法,需要调用 → this.方法名( ) {{ 方法名() }} @事件名="方法名"

  缓存特性(提升性能):
    计算属性会对计算出来的结果缓存,再次使用直接读取缓存,
    依赖项变化了,会自动重新计算 → 并再次缓存
-->

<body>
  <div id="app">
    <h3>小黑的礼物清单🛒<span>{{ totalCount }}</span></h3>
    <h3>小黑的礼物清单🛒<span>{{ fn() }}</span></h3>
    <table>
      <tr>
        <th>名字</th>
        <th>数量</th>
      </tr>
      <tr v-for="(item, index) in list" :key="item.id">
        <td>{{ item.name }}</td>
        <td>{{ item.num }}个</td>
      </tr>
    </table>

    <p>礼物总数:{{ totalCount }} 个</p>
    <p>礼物总数:{{ fn() }} 个</p>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        // 现有的数据
        list: [
          { id: 1, name: '篮球', num: 3 },
          { id: 2, name: '玩具', num: 2 },
          { id: 3, name: '铅笔', num: 5 },
        ]
      },
      computed: {
        totalCount() {
          console.log('计算属性 computed')
          let total = this.list.reduce((sum, item) => sum + item.num, 0)
          return total
        }
      },
      methods: {
        fn() {
          console.log('方法 methods')
          let total = this.list.reduce((sum, item) => sum + item.num, 0)
          return total
        }
      }
    })
  </script>
</body>

</html>

2.3 计算属性 完整写法

<!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>

<!-- 
  计算属性完整写法
    计算属性默认的简写,只能读取访问,不能 "修改"。
    如果要 "修改" → 需要写计算属性的完整写法。

    computed: {
      计算属性名: {
        get() {
          一段代码逻辑(计算逻辑)
          return 结果
        },
        set(修改的值) {
          一段代码逻辑(修改逻辑)
        }
      }
    }
-->

<body>
  <div id="app">
    姓:<input type="text" v-model="firstName"><br>
    名:<input type="text" v-model="secondName"><br>
    <p>姓名:{{nameFn}}</p>
    <button @click="fn">修改姓名</button>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        firstName: '',
        secondName: ''
      },
      computed: {
        nameFn: {
          // 读取 - 依赖别的数据计算结果
          get() {
            return this.firstName + this.secondName
          },
          // 修改 - 形参自动获取修改后的结果
          set(value) {
            // this.firstName = value.substring(0, 1)
            // this.secondName = value.substring(1)
            this.firstName = value.slice(0, 1)
            this.secondName = value.slice(1)
          }
        }
      },
      methods: {
        fn() {
          // this.nameFn = '宋江'
          this.nameFn = '林俊杰'
        }
      },
    })
  </script>
</body>

</html>

2.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" />
  <link rel="stylesheet" href="./styles/index.css" />
  <title>Document</title>
</head>

<body>
  <div id="app" class="score-case">
    <div class="table">
      <table>
        <thead>
          <tr>
            <th>编号</th>
            <th>科目</th>
            <th>成绩</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody v-if="list.length>0">
          <!-- <tr>
            <td>1</td>
            <td>语文</td>
            <td class="red">46</td>
            <td><a href="#">删除</a></td>
          </tr>
          <tr>
            <td>2</td>
            <td>英语</td>
            <td>80</td>
            <td><a href="#">删除</a></td>
          </tr>
          <tr>
            <td>3</td>
            <td>数学</td>
            <td>100</td>
            <td><a href="#">删除</a></td>
          </tr> -->
          <tr v-for="(item,index) in list" :key="item.id">
            <td>{{index+1}}</td>
            <td>{{item.subject}}</td>
            <td :class="{red:item.score<60}">{{item.score}}</td>
            <td><a href="#" @click="del(item.id)">删除</a></td>
          </tr>
        </tbody>
        <tbody v-else>
          <tr>
            <td colspan="5">
              <span class="none">暂无数据</span>
            </td>
          </tr>
        </tbody>

        <tfoot>
          <tr>
            <td colspan="5">
              <span>总分:{{total}}</span>
              <!-- toFixed(数字):保留指定的小数位数(四舍五入) -->
              <!-- <span style="margin-left: 50px">平均分:{{ (total/list.length).toFixed(2) || '0' }}</span> -->
              <span style="margin-left: 50px">平均分:{{ average }}</span>
            </td>
          </tr>
        </tfoot>
      </table>
    </div>
    <div class="form">
      <div class="form-item">
        <div class="label">科目:</div>
        <div class="input">
          <input type="text" placeholder="请输入科目" v-model.trim="subject" />
        </div>
      </div>
      <div class="form-item">
        <div class="label">分数:</div>
        <div class="input">
          <input type="text" placeholder="请输入分数" v-model.trim.number="score" />
        </div>
      </div>
      <div class="form-item">
        <div class="label"></div>
        <div class="input">
          <button class="submit" @click="add">添加</button>
        </div>
      </div>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script>
    const app = new Vue({
      el: '#app',
      data: {
        list: [
          { id: 1, subject: '语文', score: 20 },
          { id: 7, subject: '数学', score: 99 },
          { id: 12, subject: '英语', score: 70 },
        ],
        subject: '',
        score: ''
      },
      methods: {
        del(id) {
          this.list = this.list.filter(item => item.id !== id)
        },
        add() {
          if (this.subject === '' || this.score === '') {
            alert('请输入内容')
            return
          }
          this.list.unshift({
            id: +new Date(),
            subject: this.subject,
            score: this.score
          })
          this.subject = ''
          this.score = ''
        }
      },
      computed: {
        total() {
          return this.list.reduce((sum, item) => sum += item.score, 0)
        },
        average() {
          // toFixed(数字):保留指定的小数位数(四舍五入); NaN时 toFixed会转为'NaN',字符串'NaN'隐式转换为true 逻辑中断效果未达到预期
          // return (this.total / this.list.length).toFixed(2) || '0'

          // 使用Math对象的方法来对数字进行四舍五入并保留两位小数 → Math.round(num * 100) / 100
          return Math.round(this.total / this.list.length * 100) / 100 || 0
        }
      }
    })

    // || 逻辑中断
    // 如果左侧的表达式为“假值”,则返回右侧的表达式;否则,返回左侧的表达式。
    console.log(0 || 1) // 1
    console.log(NaN || 0) // 0
    console.log(undefined || NaN) // NaN

  </script>
</body>

</html>

3. watch 侦听器

<!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>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
      font-size: 18px;
    }

    #app {
      padding: 10px 20px;
    }

    .query {
      margin: 10px 0;
    }

    .box {
      display: flex;
    }

    textarea {
      width: 300px;
      height: 160px;
      font-size: 18px;
      border: 1px solid #dedede;
      outline: none;
      resize: none;
      padding: 10px;
    }

    textarea:hover {
      border: 1px solid #1589f5;
    }

    .transbox {
      width: 300px;
      height: 160px;
      background-color: #f0f0f0;
      padding: 10px;
      border: none;
    }

    .tip-box {
      width: 300px;
      height: 25px;
      line-height: 25px;
      display: flex;
    }

    .tip-box span {
      flex: 1;
      text-align: center;
    }

    .query span {
      font-size: 18px;
    }

    .input-wrap {
      position: relative;
    }

    .input-wrap span {
      position: absolute;
      right: 15px;
      bottom: 15px;
      font-size: 12px;
    }

    .input-wrap i {
      font-size: 20px;
      font-style: normal;
    }
  </style>
</head>

<!-- 
  watch 侦听器(监视器)
  作用:监视数据变化,执行一些 业务逻辑 或 异步操作。
  语法:
  1. 简单写法 → 简单类型数据,直接监视

    watch: {
      // 该方法会在数据变化时,触发执行
      数据属性名(newValue, oldValue) {
        一些业务逻辑 或 异步操作。
      },
      '对象.属性名'(newValue, oldValue) {
        一些业务逻辑 或 异步操作。
      }
    }

  2. 完整写法 → 添加额外配置项(深度监视复杂类型,立刻执行))
    deep: true 对复杂类型深度监视
    immediate: true 初始化立刻执行一次handler方法

    watch: {
      数据属性名: {
        deep: true,
        handler(newValue) {
          console.log(newValue)
        }
      }
    }
-->

<body>
  <div id="app">
    <!-- 条件选择框 -->
    <div class="query">
      <span>翻译成的语言:</span>
      <select v-model="obj.language">
        <option value="italy">意大利</option>
        <option value="english">英语</option>
        <option value="german">德语</option>
      </select>
    </div>

    <!-- 翻译框 -->
    <div class="box">
      <div class="input-wrap">
        <!-- <textarea v-model="words"></textarea> -->
        <textarea v-model="obj.words"></textarea>
        <span><i>⌨️</i>文档翻译</span>
      </div>
      <div class="output-wrap">
        <div class="transbox">{{txt}}</div>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    // 接口地址:https://applet-base-api-t.itheima.net/api/translate
    // 请求方式:get
    // 请求参数:
    // (1)words:需要被翻译的文本(必传)
    // (2)lang: 需要被翻译成的语言(可选)默认值-意大利
    // -----------------------------------------------

    const app = new Vue({
      el: '#app',
      data: {
        // words: '',
        obj: { words: '苹果', language: 'italy' },
        txt: '',
        // timer: -1
      },
      // 具体讲解:(1) watch语法 (2) 具体业务实现
      watch: {
        // 1. 简单写法
        /* words(newVal, oldVal) {
          // console.log('words', newVal, oldVal)
        }, */
        /* 'obj.words'(newVal) {
          // console.log('obj.words', newVal)

          // this.timer → timer也是挂载到了Vue实例上,只不过是普通变量,不是响应式数据
          clearTimeout(this.timer)
          this.timer = setTimeout(async () => {
            const res = await axios.get(
              'https://applet-base-api-t.itheima.net/api/translate',
              { params: { words: this.obj.words } }
            )
            this.txt = res.data.data
          }, 500)
        }, */

        // 2. 完整写法
        // 语言变 重新发请求 监听整个对象
        // obj(){}写法错误,简单数据可直接监听;复杂数据需完整写法
        obj: {
          // deep: true - 对复杂类型深度监视
          deep: true,
          // immediate: true - 初始化立刻执行一次handler方法
          immediate: true,
          handler(newVal) {
            clearTimeout(this.timer)
            this.timer = setTimeout(async () => {
              const res = await axios.get(
                'https://applet-base-api-t.itheima.net/api/translate',
                { params: { words: this.obj.words } }
              )
              this.txt = res.data.data
            }, 500)
          },
        }

      },
    })
  </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" />
  <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 class="tr" v-for="(item,index) in fruitList" :key="item.id" :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="item.num--"> - </button>
                <span class="my-input__inner">{{item.num}}</span>
                <button class="increase" @click="item.num++"> + </button>
              </div>
            </div>
            <div class="td">{{item.price*item.num}}</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="isSelectAll" />
          全选
        </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: JSON.parse(localStorage.getItem('fruit')) || [
          {
            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)
        }
      },
      // 计算属性 依赖别的数据计算
      computed: {
        // 1. 正确写法
        /* totalCount() {
          return this.fruitList.reduce((sum, item) => {
            if (item.isChecked) {
              sum += item.num
            }
            return sum
          }, 0);
        }, */
        // 2. 化简写法
        totalCount() {
          return this.fruitList.reduce((sum, item) => {
            return item.isChecked ? sum + item.num : sum
          }, 0);
        },
        // 3. 错误写法及原因
        /* totalCount() {
          return this.fruitList.reduce((sum, item) => {
            if (item.isChecked) {
              // sum += item.num
              // return sum
              // 如果 item.isChecked 为 false,reduce 方法没有返回值,那么它会直接跳过这个 item。
              // reduce 需要始终返回一个累加器,否则它会返回 undefined,导致最终的结果错误。
              // 解决办法是:无论条件是否匹配,都需要返回 sum。
              return sum += item.num
            }
          }, 0);
        }, */
        // 4. 答案写法
        /* 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) {
              sum += item.price * item.num
            }
            return sum
          }, 0);
        }, */
        totalPrice() {
          return this.fruitList.reduce((sum, item) => {
            return item.isChecked ? sum + item.price * item.num : sum
          }, 0);
        },
        isSelectAll: {
          get() {
            return this.fruitList.every(item => item.isChecked)
          },
          set(value) {
            this.fruitList.forEach(item => item.isChecked = value)
          }
        }
      },
      watch: {
        fruitList: {
          deep: true,
          handler(newVal) {
            localStorage.setItem('fruit', JSON.stringify(newVal))
          }
        }
      }
    })
  </script>
</body>

</html>

YOLOv8作物病害检测实战源码 这是一套基于YOLOv8n的农田病虫害目标检测系统,对叶片照片做目标检测,识别健康叶片、褐斑病、白粉病、锈病四类,并给出对应防治建议。全流程纯CPU可跑,配套交付完整Python源码、9章实战教程(md/html/pdf三种格式)、100张自造样本及YOLO标签、训练曲线与混淆矩阵截图、Tkinter界面和答辩PPT。 系统围绕五项功能展开:同一套推理逻辑覆盖图像、视频、摄像头三种来源;图形界面选文件即检测,画面叠彩色框标注病名与置信度,右侧面板同步给出症状与防治措施;检测记录自动写入CSV,统计页一键生成四类病害占比饼图;训练结束自动产出损失与精度曲线;内置防治建议文案库,检测命中即回填面板。 选型用YOLOv8n nano档,参数量约320万、权重约6.2MB,CPU单帧几十毫秒,小样本训练5到8分钟完成。数据由data_gen.py程序化绘制100张叶片图,固定随机种子,重跑分布一致,不依赖公开数据集。依赖全部锁版本,教程第7章整理了19条真实报错及排查方法,第8章给出换模型档、换PlantVillage数据集的迁移路径。 适合做计算机视觉方向课程设计或毕业设计的学生,也适合智慧农业方向想了解检测模型如何落进业务面板的学习者。不需要GPU,不需要系统学过目标检测,教程第3章会按需补齐原理。 使用时先pip install -r requirements.txt装好依赖,download_data.py下载预训练权重,data_gen.py生成样本后train.py训练,完成后用main.py infer批量检测或main.py gui启动界面。 立即下载

相关推荐

数据挖掘实战:决策树 + 聚类 + 关联规则,零 Key 离线可跑(Python 源码)

数据挖掘实战包。用 Python 标准库实现 CART 风格决策树、K-Means 聚类与 Apriori 关联规则,在虚构「星河便利店」会员表和购物篮上跑通分类、分群与购物篮分析,并给出准确率、轮廓系数、支持度/置信度/提升度。零第三方依赖、零 API Key 即可离线跑通。含 4 个样例、离线冒烟、中文 README(含「使用方法」)。均为作者原创可运行源码。MIT。作者 blog.youkuaiyun.com/mtl1994

产业园区运营负责人如何利用平台实现企业创新需求的精准分析与服务推送?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

科技中介服务机构在技术供需对接中如何实现服务流程的自动化与标准化?.docx

科技中介服务机构在技术供需对接中如何实现服务流程的自动化与标准化?

高校技术转移办公室人员在推动成果转化落地过程中,如何实现科技成果的高效推介与价值评估?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

产业园区如何高效对接企业技术需求与高校科研成果?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

国央企创新负责人如何快速掌握本地科技资源分布与匹配情况?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

科技中介服务机构如何通过数智工具提升技术需求挖掘的精准度?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

国央企创新负责人如何实现科技成果的精准识别与高效筛选?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

政府科技管理者如何在政策支持中更精准地识别和支持高价值科技项目?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

高校技术转移办公室人员如何系统化管理科技成果信息?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

科技中介服务机构如何构建标准化、模块化的智能服务工具?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

全桥LLC谐振变化器参数设计资料(闭环仿真+参数设计报告)

内容概要:本文档系统性地介绍了全桥LLC谐振变换器的闭环仿真建模与关键参数设计方法,涵盖其工作原理、谐振元件(如谐振电感、谐振电容、励磁电感)的计算与优化流程,并结合Simulink或Plecs等仿真平台构建闭环控制系统模型,重点验证变频控制策略下的软开关实现、系统增益特性、效率优化及动态响应性能。资料包含完整的仿真模型、参数设计报告、数学计算书及参考文献,支持从理论分析到工程实践的全流程研究,适用于高频高效DC-DC变换器的研发与教学应用。; 适合人群:电力电子、电气工程及其自动化等相关专业的高校学生、科研人员,以及从事开关电源、新能源变换器、工业电源研发的工程技术人员,具备电路拓扑基础与仿真软件操作能力者更佳。; 使用场景及目标:①深入掌握全桥LLC谐振变换器的工作机理与谐振参数设计准则;②学习并实现闭环反馈控制系统的建模与稳定性分析;③支撑科研课题、课程设计、毕业设计或实际产品开发中的拓扑选型与性能验证需求;④通过仿真与计算联动提升对软开关技术与效率优化的理解与应用能力。; 阅读建议:建议结合所提供的仿真文件与设计文档同步操作,按照“建模—参数计算—仿真验证—性能分析”的流程逐步推进,重点关注频率调节对ZVS/ZCS实现的影响,对比不同负载条件下的闭环响应差异,深化对谐振腔特性和控制策略协同作用的理解。

fab-pm-optimizer-python-entry-py39-20261021-v1-1-0

fab-pm-optimizer-python-entry-py39-20261021-v1-1-0

政府科技管理者如何提升区域科技成果转化项目的评价与筛选效率?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

PSIM-三相维也纳VIENNA整流器电压电流双闭环+中点电位平衡SVPWM控制仿真

内容概要:本文档详细介绍了一种基于PSIM软件实现的三相维也纳(VIENNA)整流器仿真模型,重点实现了电压电流双闭环控制与中点电位平衡的SVPWM调制策略。该仿真系统通过精确的控制算法有效提升了整流器的功率因数,降低了输入电流谐波含量,并解决了三电平拓扑中常见的直流侧中点电位偏移问题,具有较高的工程实用价值,适用于高效率、高功率密度的电力电子系统设计与科研验证。文档还整合了丰富的配套资源,涵盖多种电力电子变换器拓扑结构、先进控制算法及典型应用场景,形成了系统化的技术资料体系,有助于深入理解现代整流技术的核心原理与发展动态。; 适合人群:面向从事电力电子、电气工程、新能源变换系统等领域的科研人员、高校研究生以及具备一定仿真基础的工程技术人员。; 使用场景及目标:①深入研究三相VIENNA整流器的工作机理与控制特性;②掌握电压外环、电流内环双闭环控制结构的设计方法;③理解并实现SVPWM调制技术与中点电位平衡控制策略的协同作用;④开展相关控制算法的仿真验证、性能优化与实验对比,为高性能整流装置的研发提供理论支撑和技术原型。; 阅读建议:建议结合文中列出的其他仿真案例(如PWM整流器、LLC谐振变换器、DAB变换器等)进行横向对比学习,充分利用所提供的PSIM、MATLAB/Simulink等多平台仿真资源,重点关注控制逻辑与主电路的耦合关系,通过参数调试与波形分析深化对系统动态响应和稳态性能的理解,强化“理论—建模—仿真—优化”的闭环研究能力。

产业园区运营负责人如何利用数智工具提升园区企业的技术对接效率?.docx

科易网基于AI模型+行业大数据,在技术转移、成果转化、技术经纪、知识产权、产业创新、科技招商等垂直领域的多样化应用场景,推出科技创新领域的AI+工具与数智化解决方案,推动科技创新与产业创新智能化发展。

jp-host-release.apk

jp-host-release.apk

ICD10 openGauss版

ICD10 openGauss版

上一篇: 二五(Vue2-01)、创建实例、插值表达式、响应式、Vue指令(v-show v-if v-on v-bind v-for v-model)
下一篇: 二七(vue2-03)、生命周期四个阶段及八个钩子、工程化开发和脚手架、组件注册、拆分组件
bpmf_fff
博客等级 码龄6年 219粉丝 49原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值