Lecture14-中型页面串讲练习

文章目录

    [toc]

    单词词性音标含义
    TodoAppn./ˈtuːduː æp/本节综合练习的待办事项组件
    computedadj./kəmˈpjuːtɪd/计算属性,由其他响应式数据派生(如未完成计数)
    refn./ref/声明响应式数据的 API,数组需重新赋值才触发更新
    v-for-/viː fɔː(r)/Vue 的列表渲染指令,按数组生成多项
    filterv./ˈfɪltə(r)/数组过滤方法,常用来生成新数组实现删除
    scopedadj./ˈskəʊpt/样式只作用于当前组件,防止污染别处
    Todon./ˈtuːduː/待办项的类型定义(id / title / done)

    Lecture14-中型页面串讲练习

    1、本节讲什么

    把前面内容串起来,做一个**"待办事项"小页面**:能添加、勾选、删除,数据存内存。这是 Vue 部分的综合练习——重点是顺手把第 11 节的栅格布局、第 13 节的 Flex 用起来。

    2、概念速览

    本节定位:综合练习,把前面学的模板、响应式、循环、布局拼成一个完整页面,目的是"串起来用",不是学新概念。

    布局怎么串:页面骨架用 el-row / el-col 划分(第 11 节),骨架内的细节用 Flex 排(第 13 节),样式写在 <style scoped>(第 13 节)——正是上一节理的"各管一段"分工。

    类比:像把几个方法拼成一个完整功能。

    正确 vs 误区:别追求一次写对,重点是"能拆成小步骤、每步验证"——这正是 Vibe Coding 的节奏。

    3、先看效果

    image-20261008171117827

    一个"输入框 + 按钮"在顶部一行、下面是列表,每条待办一行:勾选框 + 文字 + 删除按钮。骨架用 el-row/el-col,行内用 Flex。

    src/TodoApp.vue

    <script setup lang="ts">
    import { ref, computed } from 'vue'
    import { ElMessage } from 'element-plus'
    
    interface Todo { id: number; title: string; done: boolean }
    
    const todos = ref<Todo[]>([
      { id: 1, title: '学习 Vue', done: false },
      { id: 2, title: '学习 TS', done: true },
    ])
    const input = ref('')
    
    const undoneCount = computed(() => todos.value.filter(t => !t.done).length)
    
    function add() {
      if (!input.value.trim()) {
        ElMessage.warning('先输入内容')   // 空输入提醒
        return
      }
      todos.value.push({ id: Date.now(), title: input.value, done: false })
      input.value = ''
    }
    function remove(id: number) {
      todos.value = todos.value.filter(t => t.id !== id)
    }
    </script>
    
    <template>
      <!-- ① 页面骨架:el-row 一行,各块用 el-col 划分 -->
      <el-row :gutter="16">
        <el-col :span="24">
          <h2>待办事项</h2>
        </el-col>
    
        <!-- ② 输入区:el-col 内用 flex 把输入框和按钮排成一行 -->
        <el-col :span="24">
          <div class="add-bar">
            <el-input v-model="input" placeholder="输入待办,回车添加" @keyup.enter="add" />
            <el-button type="primary" @click="add">添加</el-button>
          </div>
        </el-col>
    
        <!-- ③ 状态 + 列表区 -->
        <el-col :span="24">
          <p>还剩 {{ undoneCount }} 项未完成</p>
          <ul>
            <li v-for="t in todos" :key="t.id" class="todo-item">
              <input type="checkbox" v-model="t.done" />
              <span :style="{ textDecoration: t.done ? 'line-through' : 'none' }" class="title">{{ t.title }}</span>
              <el-button size="small" type="danger" @click="remove(t.id)">删除</el-button>
            </li>
          </ul>
        </el-col>
      </el-row>
    </template>
    
    <style scoped>
    /* ② 输入区:flex 排一行,输入框撑满、按钮固定右侧(flex: 1 见第 21 节) */
    .add-bar { display: flex; gap: 8px; }
    .add-bar .el-input { flex: 1; }
    
    /* ③ 每条待办:flex 排一行,勾选框 + 文字 + 删除按钮 */
    .todo-item { display: flex; align-items: center; gap: 8px; }
    .todo-item .title { flex: 1; }
    </style>
    

    4、照着做

    1. 建 TodoApp.vue,替换 App.vue,跑起来。
    2. 看布局:el-row/el-col 把页面分成哪几块?add-bar 和 todo-item 里的 Flex 各排了什么?
    3. 把"添加 / 勾选 / 删除 / 计数"四件事都亲手做一遍,确认都能用。

    5、常见疑问

    • Q:为什么用 ref<Todo[]> 这种写法? A:给数组定类型,TS 才能帮你检查 push 的对象字段对不对——这就是第 6 节类型的实战价值。
    • Q:为什么要把输入框和按钮包一层 .add-bar 再 flex? A:el-input 和 el-button 是 Element Plus 组件,它们本身是块级组件,直接并排放不下。包一个普通 div 当 flex 容器,就能用 flex + gap 控制它们在一行、还留间距——这是"在 el-col 里用 flex 排组件"的标准做法。
    • Q:删除用 filter 重新赋值,为什么不是 splice? A:改 ref 数组要触发响应式,用 filter 生成新数组重新赋值最稳。splice 直接改原数组有时不触发更新。记住"改 ref 数组就重新赋值"。

    6、规范要点

    • 本节的 TodoApp 是"一个组件干一件事"的好例子:逻辑在脚本里、结构在模板里、样式在 style 里,三者分清楚。
    • 骨架用 el-row/el-col,行内细节用 Flex,样式带 scoped——这一节你已经把第 20、22 节的布局技术完整用了一遍。
    • 数据操作(添加/删除)写成带名字的函数(add/remove),别在模板里写一堆内联逻辑。
    • 到这里你已经能读懂并修改一个"有布局、有交互"的完整中型 Vue 页面——接下来交给 AI 前,先自己能跑通。

    7、本节验证

    • 能独立跑通 TodoApp 的添加 / 勾选 / 删除 / 计数 ✅
    • 会用 el-row / el-col 搭骨架、列内用 flex 排细节 ✅
    • 会用 ref<Todo[]> 给数组定类型 ✅
    • 知道改 ref 数组要用 filter 重新赋值触发响应式 ✅
    end