Day02_AJAX 综合案例

目录

知识点自测

目录

学习目标

01.案例_图书管理-介绍

目标

讲解

小结

02.Bootstrap 弹框_属性控制

目标

讲解

小结

03.Bootstrap 弹框_JS控制

目标

讲解

小结

04.案例_图书管理_渲染列表

目标

讲解

小结

05.案例_图书管理_新增图书

目标

讲解

小结

06.案例_图书管理_删除图书

目标

讲解

小结

07-09.案例_图书管理_编辑图书

目标

讲解

小结

10.案例_图书管理_总结

目标

讲解

小结

11.图片上传

目标

讲解

小结

12.案例_网站-更换背景图

目标

讲解

小结

13.案例_个人信息设置-介绍

目标

讲解

小结

14.案例_个人信息设置-信息渲染

目标

讲解

小结

15.案例_个人信息设置-头像修改

目标

讲解

小结

16.案例_个人信息设置-信息修改

目标

讲解

小结

17.案例_个人信息设置-提示框

目标

讲解

小结

参考文献


知识点自测

  1. 以下代码运行结果是什么?(考察扩展运算符的使用)

    const result = {
      name: '老李',
      age: 18
    }
    const obj = {
      ...result
    }
    console.log(obj.age)

    A:报错

    B:18

    ​答案
    • B正确

  1. 什么是事件委托?

    A:只能把单击事件委托给父元素绑定

    B:可以把能冒泡的事件,委托给已存在的向上的任意标签元素绑定

    ​答案
    • B正确

  1. 事件对象e.target作用是什么?

    A:获取到这次触发事件相关的信息

    B:获取到这次触发事件目标标签元素

    ​答案
    • B正确

  1. 如果获取绑定在标签上自定义属性的值10?

    <div data-code="10">西游记</div>

    A:div标签对象.innerHTML

    B:div标签对象.dataset.code

    C:div标签对象.code

    ​答案
    • B正确

  1. 哪个方法可以判断目标标签是否包含指定的类名?

    <div class="my-div title info"></div>

    A: div标签对象.className === 'title'

    B: div标签对象.classList.contains('title')

    ​答案
    • B正确

  1. 伪数组取值哪种方式是正确的?

    let obj = { 0: '老李', 1: '老刘' }

    A: obj.0

    B: obj[0]

    ​答案
    • B正确

  1. 以下哪个选项可以,往本地存储键为‘bgImg’,值为图片url网址的代码

    A:localStorage.setItem('bgImg')

    B:localStorage.getItem('bgImg')

    C:localStorage.setItem('bgImg', '图片url网址')

    D:localStorage.getItem('bgImg', '图片url网址')

    ​答案
    • C正确

  1. 以下代码运行结果是?

    const obj = {
      username: '老李',
      age: 18,
      sex: '男'
    }
    Object.keys(obj)

    A:代码报错

    B:[username, age, sex]

    C:["username", "age", "sex"]

    D:["老李", 18, "男"]

    ​答案
    • C正确

  1. 下面哪个选项可以把数字字符串转成数字类型?

    A:+’10‘

    B:’10‘ + 0

    ​答案
    • A正确

  1. 以下代码运行后的结果是什么?(考察逻辑与的短路特性)

    const age = 18
    const result1 = (age || '有年龄')
    ​
    const sex = ''
    const result2 = sex || '没有性别'

    A:报错,报错

    B:18,没有性别

    C:有年龄,没有性别

    D:18,’‘

    ​答案
    • B正确

学习目标

今天主要就是练,巩固 axios 的使用

  1. 完成案例-图书管理系统(增删改查)经典业务

  2. 掌握图片上传的思路

  3. 完成案例-网站换肤并实现图片地址缓存

  4. 完成案例-个人信息设置

01.案例_图书管理-介绍

目标

  • 案例-图书管理-介绍(介绍要完成的效果和练习到的思维)

讲解

  1. 打开备课代码运行图书管理案例效果-介绍要完成的增删改查业务效果和 Bootstrap 弹框使用

  2. 分析步骤和对应的视频模块

    • 先学习 Bootstrap 弹框的使用(因为添加图书和编辑图书需要这个窗口来承载图书表单)

    • 先做渲染图书列表(这样做添加和编辑以及删除可以看到数据变化,所以先做渲染)

    • 再做新增图书功能

    • 再做删除图书功能

    • 再做编辑图书功能(注意:编辑和新增图书是2套弹框-后续做项目我们再用同1个弹框)

代码演示:

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>案例-图书管理</title>
  <!-- 字体图标 -->
  <link rel="stylesheet" href="https://at.alicdn.com/t/c/font_3736758_vxpb728fcyh.css">
  <!-- 引入bootstrap.css -->
  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet">
  <!-- 核心样式 -->
  <link rel="stylesheet" href="./css/index.css">
</head>

<body>
  <!-- 主体区域 -->
  <div class="container">
    <!-- 头部标题和添加按钮 -->
    <div class="top">
      <h3>图书管理</h3>
      <button type="button" class="btn btn-primary plus-btn" data-bs-toggle="modal" data-bs-target=".add-modal"> + 添加
      </button>
    </div>
    <!-- 数据列表 -->
    <table class="table">
      <thead class="table-light">
        <tr>
          <th style="width: 150px;">序号</th>
          <th>书名</th>
          <th>作者</th>
          <th>出版社</th>
          <th style="width: 180px;">操作</th>
        </tr>
      </thead>
      <tbody class="list">
        <!-- <tr>
          <td>1</td>
          <td>JavaScript程序设计</td>
          <td>马特·弗里斯比</td>
          <td>人民邮电出版社</td>
          <td>
            <span class="del">删除</span>
            <span class="edit">编辑</span>
          </td>
        </tr> -->
      </tbody>
    </table>
  </div>
  <!-- 新增-弹出框 -->
  <div class="modal fade add-modal">
    <!-- 中间白色区域 -->
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header top">
          <span>添加图书</span>
          <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="modal"></button>
        </div>
        <div class="modal-body form-wrap">
          <!-- 新增表单 -->
          <form class="add-form">
            <div class="mb-3">
              <label for="bookname" class="form-label">书名</label>
              <input type="text" class="form-control bookname" placeholder="请输入书籍名称" name="bookname">
            </div>
            <div class="mb-3">
              <label for="author" class="form-label">作者</label>
              <input type="text" class="form-control author" placeholder="请输入作者名称" name="author">
            </div>
            <div class="mb-3">
              <label for="publisher" class="form-label">出版社</label>
              <input type="text" class="form-control publisher" placeholder="请输入出版社名称" name="publisher">
            </div>
          </form>
        </div>
        <div class="modal-footer btn-group">
          <button type="button" class="btn btn-primary" data-bs-dismiss="modal"> 取消 </button>
          <button type="button" class="btn btn-primary add-btn"> 保存 </button>
        </div>
      </div>
    </div>
  </div>
  <!-- 编辑-弹出框 -->
  <div class="modal fade edit-modal">
    <!-- 中间白色区域 -->
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header top">
          <span>编辑图书</span>
          <button type="button" class="btn-close" aria-label="Close" data-bs-dismiss="modal"></button>
        </div>
        <div class="modal-body form-wrap">
          <!-- 编辑表单 -->
          <form class="edit-form">
            <input type="hidden" class="id" name="id">
            <div class="mb-3">
              <label for="bookname" class="form-label">书名</label>
              <input type="text" class="form-control bookname" placeholder="请输入书籍名称" name="bookname">
            </div>
            <div class="mb-3">
              <label for="author" class="form-label">作者</label>
              <input type="text" class="form-control author" placeholder="请输入作者名称" name="author">
            </div>
            <div class="mb-3">
              <label for="publisher" class="form-label">出版社</label>
              <input type="text" class="form-control publisher" placeholder="请输入出版社名称" name="publisher">
            </div>
          </form>
        </div>
        <div class="modal-footer btn-group">
          <button type="button" class="btn btn-primary" data-bs-dismiss="modal"> 取消 </button>
          <button type="button" class="btn btn-primary edit-btn"> 修改 </button>
        </div>
      </div>
    </div>
  </div>
  <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.2.0/axios.min.js"></script>
  <script src="./lib/form-serialize.js"></script>
  <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/js/bootstrap.min.js"></script>
  <!-- 核心逻辑 -->
  <script src="./js/index.js"></script>
</body>

</html>

css代码

<!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>Bootstrap 弹框</title>
  <!-- 引入bootstrap.css -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
  <!-- 
    目标:使用Bootstrap弹框
    1. 引入bootstrap.css 和 bootstrap.js
    2. 准备弹框标签,确认结构
    3. 通过自定义属性,控制弹框的显示和隐藏
   -->
  <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".my-box">
    显示弹框
  </button>

  <!-- 
    弹框标签
    bootstrap的modal弹框,添加modal类名(默认隐藏)
   -->
  <div class="modal my-box" tabindex="-1">
    <div class="modal-dialog">
      <!-- 弹框-内容 -->
      <div class="modal-content">
        <!-- 弹框-头部 -->
        <div class="modal-header">
          <h5 class="modal-title">Modal title</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <!-- 弹框-身体 -->
        <div class="modal-body">
          <p>Modal body text goes here.</p>
        </div>
        <!-- 弹框-底部 -->
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
          <button type="button" class="btn btn-primary">Save changes</button>
        </div>
      </div>
    </div>
  </div>

  <!-- 引入bootstrap.js -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script>
</body>

</html>

要引入的js代码

到官网: https://www.npmjs.com/package/form-serialize

查找js文件

小结

  1. 做完这个案例我们将会有什么收获呢?

    ​答案​ : 掌握前端经典增删改查的业务和思路,对以后开发很有帮助

02.Bootstrap 弹框_属性控制

目标

  • 使用属性方式控制 Bootstarp 弹框的显示和隐藏

讲解

  1. 什么是 Bootstrap 弹框?

    • 不离开当前页面,显示单独内容,供用户操作

  2. 如何使用 Bootstrap 弹框呢?

    • 先引入 bootstrap.css 和 bootstrap.js 到自己网页中

    • 准备弹框标签,确认结构(可以从文档的 Modal 里复制基础例子)

    • 通过自定义属性,通知弹框的显示和隐藏,语法如下:

  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>Bootstrap 弹框</title>
  <!-- 引入bootstrap.css -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
  <!-- 
    目标:使用Bootstrap弹框
    1. 引入bootstrap.css 和 bootstrap.js
    2. 准备弹框标签,确认结构
    3. 通过自定义属性,控制弹框的显示和隐藏
   -->
  <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".my-box">
    显示弹框
  </button>

  <!-- 
    弹框标签
    bootstrap的modal弹框,添加modal类名(默认隐藏)
   -->
  <div class="modal my-box" tabindex="-1">
    <div class="modal-dialog">
      <!-- 弹框-内容 -->
      <div class="modal-content">
        <!-- 弹框-头部 -->
        <div class="modal-header">
          <h5 class="modal-title">Modal title</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <!-- 弹框-身体 -->
        <div class="modal-body">
          <p>Modal body text goes here.</p>
        </div>
        <!-- 弹框-底部 -->
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
          <button type="button" class="btn btn-primary">Save changes</button>
        </div>
      </div>
    </div>
  </div>

  <!-- 引入bootstrap.js -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script>
</body>

</html>

小结

  1. 用哪个属性绑定来控制弹框显示呢?

    ​答案​ : data-bs-toggle和data-bs-target
  2. 用哪个属性来控制隐藏弹框呢?

    ​答案​ : data-bs-dismiss 关闭的是标签所在的当前提示框

03.Bootstrap 弹框_JS控制

目标

  • 使用JS方式控制 Bootstarp 弹框的显示和隐藏

讲解

  1. 为什么需要 JS 方式控制呢?

    • 当我显示之前,隐藏之前,需要执行一些 JS 逻辑代码,就需要引入 JS 控制弹框显示/隐藏的方式了

  2. 如何使用 JS 方式 控制 Bootstrap 弹框显示和隐藏呢?

    语法如下:

  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>Bootstrap 弹框</title>
    <!-- 引入bootstrap.css -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">
</head>

<body>
    <!-- 
    目标:使用JS控制弹框,显示和隐藏
    1. 创建弹框对象
    2. 调用弹框对象内置方法
      .show() 显示
      .hide() 隐藏
   -->
    <button type="button" class="btn btn-primary edit-btn">
        编辑姓名
    </button>

    <div class="modal name-box" tabindex="-1">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title">请输入姓名</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <form action="">
                        <span>姓名:</span>
                        <input type="text" class="username">
                    </form>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
                    <button type="button" class="btn btn-primary save-btn">保存</button>
                </div>
            </div>
        </div>
    </div>

    <!-- 引入bootstrap.js -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script>
    <script>
        // 1. 创建弹框对象
        const modalDom = document.querySelector('.name-box')
        const modal = new bootstrap.Modal(modalDom)
        // 编辑姓名->点击->赋予默认姓名->弹框显示
        document.querySelector('.edit-btn').addEventListener('click', () => {
            // 2. 显示弹框
            modal.show()
            document.querySelector('.username').value = '默认姓名'
        })

        // 保存->点击->->获取姓名打印->弹框隐藏
        document.querySelector('.save-btn').addEventListener('click', () => {
            const username = document.querySelector('.username').value
            console.log('模拟把姓名保存到服务器上', username)

            // 2. 隐藏弹框
            modal.hide()
        })
    </script>
</body>

</html>

小结

  1. 什么时候用属性控制,什么时候用 JS 控制 Bootstrap 弹框的显示/隐藏?

    ​答案​ : 直接出现/隐藏用属性方式控制,如果需要先执行一段 JS 逻辑再显示/隐藏就用 JS 方式控制

04.案例_图书管理_渲染列表

目标

  • 完成图书管理案例-图书列表数据渲染效果

讲解

  1. 为什么需要给自己起一个外号呢?

    • 我们所有人数据都来自同一个服务器上,为了区分每个同学不同的数据,需要大家设置一个外号告诉服务器,服务器就会返回你对应的图书数据了

  2. 完成渲染列表的思路步骤?

    1. 基于 axios 和接口文档获取到图书列表数据

    2. 分析数据结构和标签对应关系,把数据展示到页面上(因为有多处使用-所以可以封装个函数,外号也有多处使用,可以提升到全局常量)

代码演示:

/**
 * 目标1:渲染图书列表
 *  1.1 获取数据
 *  1.2 渲染数据
 */
const creator = '小聪'
const list = document.querySelector('.list')
// 封装-获取并渲染图书列表函数
function getBooksList() {
  // 1.1 获取数据
  axios({
    url: 'http://hmajax.itheima.net/api/books',
    params: {
      // 外号:获取对应数据
      creator
    }
  }).then(result => {
    // console.log(result)
    const bookList = result.data.data
    // console.log(bookList)
    // 1.2 渲染数据
    list.innerHTML = bookList.map((item, index) => {
      return `<tr>
      <td>${index + 1}</td>
      <td>${item.bookname}</td>
      <td>${item.author}</td>
      <td>${item.publisher}</td>
      <td data-id=${item.id}>
        <span class="del">删除</span>
        <span class="edit">编辑</span>
      </td>
    </tr>`
    }).join('')
  })
}
// 网页加载运行,获取并渲染列表一次
getBooksList()

小结

  1. 渲染数据列表的2个步骤是什么?

    ​答案​ : 获取数据,分析结构渲染到页面上

05.案例_图书管理_新增图书

目标

  • 完成图书管理案例-新增图书需求

讲解

  1. 完成新增图书的思路步骤?

    1. 先控制新增图书弹框的显示和隐藏(基于 Bootstrap 弹框和准备好的表单-用属性和 JS 方式控制)

    2. 在点击添加确认按钮时,收集新增图书表单的数据,提交到服务器

    3. 刷新图书列表(重新调用下之前封装的获取并渲染列表的函数)

代码演示:

/**
 * 目标2:新增图书
 *  2.1 新增弹框->显示和隐藏
 *  2.2 收集表单数据,并提交到服务器保存
 *  2.3 刷新图书列表
 */
// 2.1 创建弹框对象
const addModalDom = document.querySelector('.add-modal')
const addModal = new bootstrap.Modal(addModalDom)
// 保存按钮->点击->隐藏弹框
document.querySelector('.add-btn').addEventListener('click', () => {
  // 2.2 收集表单数据,并提交到服务器保存
  const addForm = document.querySelector('.add-form')
  const bookObj = serialize(addForm, { hash: true, empty: true })
  // console.log(bookObj)
  // 提交到服务器
  axios({
    url: 'http://hmajax.itheima.net/api/books',
    method: 'POST',
    data: {
      ...bookObj,
      creator
    }
  }).then(result => {
    // console.log(result)
    // 2.3 添加成功后,重新请求并渲染图书列表
    getBooksList()
    // 重置表单
    addForm.reset()
    // 隐藏弹框
    addModal.hide()
  })
})

小结

  1. 新增数据的3个步骤是什么?

    ​答案​ : 准备好数据标签和样式,然后收集表单数据提交保存,刷新列表

06.案例_图书管理_删除图书

目标

  • 完成图书管理案例-删除图书需求

讲解

  1. 完成删除图书的思路步骤?

    1. 给删除元素,绑定点击事件(事件委托方式并判断点击的是删除元素才走删除逻辑代码),并获取到要删除的数据id

    2. 基于 axios 和接口文档,调用删除接口,让服务器删除这条数据

    3. 重新获取并刷新图书列表

代码演示:

/**
 * 目标3:删除图书
 *  3.1 删除元素绑定点击事件->获取图书id
 *  3.2 调用删除接口
 *  3.3 刷新图书列表
 */
// 3.1 删除元素->点击(事件委托)
list.addEventListener('click', e => {
  // 获取触发事件目标元素
  // console.log(e.target)
  // 判断点击的是删除元素
  if (e.target.classList.contains('del')) {
    // console.log('点击删除元素')
    // 获取图书id(自定义属性id)
    const theId = e.target.parentNode.dataset.id
    // console.log(theId)
    // 3.2 调用删除接口
    axios({
      //路径参数
      url: `http://hmajax.itheima.net/api/books/${theId}`,
      method: 'DELETE'
    }).then(() => {
      // 3.3 刷新图书列表
      getBooksList()
    })
  }
})

小结

  1. 删除数据的步骤是什么?

    ​答案​ : 告知服务器要删除的数据id,服务器删除后,重新获取并刷新列表

07-09.案例_图书管理_编辑图书

目标

  • 完成图书管理案例-编辑图书需求

讲解

  1. 因为编辑图书要做回显等,比较复杂,所以分了3个视频来讲解

代码演示:

/**
 * 目标4:编辑图书
 *  4.1 编辑弹框->显示和隐藏
 *  4.2 获取当前编辑图书数据->回显到编辑表单中
 *  4.3 提交保存修改,并刷新列表
 */
// 4.1 编辑弹框->显示和隐藏
const editModal = new bootstrap.Modal('.edit-modal')
// 编辑元素->点击->弹框显示
list.addEventListener('click', e => {
  // 判断点击的是否为编辑元素
  if (e.target.classList.contains('edit')) {
    // 4.2 获取当前编辑图书数据->回显到编辑表单中
    const theId = e.target.parentNode.dataset.id
    axios({
      url: `http://hmajax.itheima.net/api/books/${theId}`
    }).then(result => {
      const bookObj = result.data.data
      // document.querySelector('.edit-form .bookname').value = bookObj.bookname
      // document.querySelector('.edit-form .author').value = bookObj.author
      // 数据对象“属性”和标签“类名”一致
      // 遍历数据对象,使用属性去获取对应的标签,快速赋值
      const keys = Object.keys(bookObj) // ['id', 'bookname', 'author', 'publisher']
      keys.forEach(key => {
        document.querySelector(`.edit-form .${key}`).value = bookObj[key]
      })
    })
    editModal.show()
  }
})

// 修改按钮->点击->隐藏弹框
document.querySelector('.edit-btn').addEventListener('click', () => {
  // 4.3 提交保存修改,并刷新列表
  const editForm = document.querySelector('.edit-form')
  const { id, bookname, author, publisher } = serialize(editForm, { hash: true, empty: true })
  // 保存正在编辑的图书id,隐藏起来:无需让用户修改
  // <input type="hidden" class="id" name="id" value="84783">
  axios({
    url: `http://hmajax.itheima.net/api/books/${id}`,
    method: 'PUT',
    data: {
      bookname,
      author,
      publisher,
      creator
    }
  }).then(() => {
    // 修改成功以后,重新获取并刷新列表
    getBooksList()

    // 隐藏弹框
    editModal.hide()
  })
})

小结

  1. 编辑数据的步骤是什么?

    ​答案​ : 获取正在编辑数据并回显,收集编辑表单的数据提交保存,重新获取并刷新列表

10.案例_图书管理_总结

目标

  • 总结下增删改查的核心思路

讲解

把之前讲解的思路在重新的总结一遍

小结

  1. 学完图书管理案例,我们收货了什么?

    ​答案​ : 现在编辑的虽然是图书数据,以后编辑其他数据,再做增删改查都是差不多的思路

11.图片上传

目标

  • 把本地图片上传到网页上显示

讲解

  1. 什么是图片上传?

    • 就是把本地的图片上传到网页上显示

  2. 图片上传怎么做?

    • 先依靠文件选择元素获取用户选择的本地文件,接着提交到服务器保存,服务器会返回图片的 url 网址,然后把网址加载到 img 标签的 src 属性中即可显示

  3. 为什么不直接显示到浏览器上,要放到服务器上呢?

    • 因为浏览器保存是临时的,如果你想随时随地访问图片,需要上传到服务器上

  4. 图片上传怎么做呢?

    1. 先获取图片文件对象

    2. 使用 FormData 表单数据对象装入(因为图片是文件而不是以前的数字和字符串了所以传递文件一般需要放入 FormData 以键值对-文件流的数据传递(可以查看请求体-确认请求体结构)

    3. 提交表单数据对象,服务器返回图片 url 网址

  5. 到代码区尝试一下

<!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>图片上传</title>
</head>

<body>
  <!-- 文件选择元素 -->
  <input type="file" class="upload">
  <img src="" alt="" class="my-img">

  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script>
    /**
     * 目标:图片上传,显示到网页上
     *  1. 获取图片文件
     *  2. 使用 FormData 携带图片文件
     *  3. 提交到服务器,获取图片url网址使用
    */
    // 文件选择元素->change改变事件
    document.querySelector('.upload').addEventListener('change', e => {
      // 1. 获取图片文件
      console.log(e.target.files[0])
      // 2. 使用 FormData 携带图片文件
      const data = new FormData()
      data.append('img', e.target.files[0])
      // 3. 提交到服务器,获取图片url网址使用
      axios({
        url: 'http://hmajax.itheima.net/api/uploadimg',
        method: 'POST',
        data
      }).then(result => {
        console.log(result)
        // 取出图片url网址,用img标签加载显示
        const imgUrl = result.data.data.url
        document.querySelector('.my-img').src = imgUrl
      })
    })
  </script>
</body>

</html>

小结

  1. 图片上传的思路是什么?

    ​答案​ : 先用文件选择元素,获取到文件对象,然后装入 FormData 表单对象中,再发给服务器,得到图片在服务器的 URL 网址,再通过 img 标签加载图片显示

12.案例_网站-更换背景图

目标

  • 实现更换网站背景图的效果

讲解

  1. 先运行备课代码,查看要完成的效果

  2. 网站更换背景图如何实现呢,并且保证刷新后背景图还在?

    1. 先获取到用户选择的背景图片,上传并把服务器返回的图片 url 网址设置给 body 背景

    2. 上传成功时,保存图片 url 网址到 localStorage 中

    3. 网页运行后,获取 localStorage 中的图片的 url 网址使用(并判断本地有图片 url 网址字符串才设置)

  3. 代码区实现下

css样式

html,
body {
  height: 100%;
  font-size: 14px;
}

.nav {
  height: 60px;
  background: rgba(0, 0, 0, 0.2);
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 20px;
}

.nav ul {
  display: flex;
}

.nav ul li {
  margin-right: 10px;
  cursor: pointer;
}

.nav ul li a {
  color: white;
  text-decoration: none;
}

.nav .right label {
  background: #edf2f5;
  padding: 5px 10px;
  border-radius: 10px;
  font-size: 12px;
  cursor: pointer;
}

.nav .right input {
  display: none;
}

.search-container {
  position: absolute;
  top: 30%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
}

.search-container .search-box {
  display: flex;
}

.search-container img {
  margin-bottom: 30px;
}


.search-container .search-box input {
  width: 512px;
  height: 16px;
  padding: 12px 16px;
  font-size: 16px;
  margin: 0;
  vertical-align: top;
  outline: 0;
  box-shadow: none;
  border-radius: 10px 0 0 10px;
  border: 2px solid #c4c7ce;
  background: #fff;
  color: #222;
  overflow: hidden;
  box-sizing: content-box;
  -webkit-tap-highlight-color: transparent;
}

.search-container .search-box button {
  cursor: pointer;
  width: 112px;
  height: 44px;
  line-height: 41px;
  line-height: 42px;
  background-color: #ad2a27;
  border-radius: 0 10px 10px 0;
  font-size: 17px;
  box-shadow: none;
  font-weight: 400;
  border: 0;
  outline: 0;
  letter-spacing: normal;
  color: white;
}

body {
  background: no-repeat center /cover;
  background-color: #edf0f5;
}

index.html

<!DOCTYPE html>
<html lang="zh-CN">

<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>网站-更换背景</title>
  <!-- 初始化样式 -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset.css@2.0.2/reset.min.css">
  <!-- 核心样式 -->
  <link rel="stylesheet" href="./css/index.css">
</head>

<body>
  <div class="container">
    <div class="nav">
      <div class="left">
        <ul>
          <li><a href="http://yun.itheima.com/?webzly" target="_blank" rel="nofollow">免费教程</a></li>
          <li><a href="http://resource.ityxb.com/booklist/?webzly" target="_blank" rel="nofollow">原创书籍</a></li>
          <li><a href="http://www.itheima.com/teacher.html?webzly#ajavaee" target="_blank" rel="nofollow">教研团队</a></li>
          <li><a href="http://www.itheima.com/special/hmschool/index.shtml?webzly" target="_blank" rel="nofollow">校区汇总</a></li>
          <li><a href="http://www.itheima.com/flow/flow.html?webzly" target="_blank" rel="nofollow">报名流程</a></li>
          <li><a href="https://pip.itcast.cn?hmgw$webzly" target="_blank" rel="nofollow">项目信息站</a></li>
          <li><a href="http://bbs.itheima.com/forum.php?webzly" target="_blank" rel="nofollow">技术社区</a></li>
        </ul>
      </div>
      <div class="right">
        <label for="bg">更换背景</label>
        <input class="bg-ipt" type="file" id="bg">
      </div>
    </div>
    <div class="search-container">
      <img src="https://www.itheima.com/images/logo.png" alt="">
      <div class="search-box">
        <input type="text">
        <button>搜索一下</button>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <!-- 核心代码 -->
  <script src="./js/index.js"></script>
</body>

</html>

index.js

/**
 * 目标:网站-更换背景
 *  1. 选择图片上传,设置body背景
 *  2. 上传成功时,"保存"图片url网址
 *  3. 网页运行后,"获取"url网址使用
 * */
// 获取并添加事件
document.querySelector('#bg').addEventListener('change',e=>{
  // 选择文件对象
  const data = new FormData()
  data.append('img',e.target.files[0])
  axios({
    url:'http://hmajax.itheima.net/api/uploadimg',
    method:'post',
    data
  }).then(res =>{
    document.body.style.backgroundImage = `url(${res.data.data.url})`
    localStorage.setItem('bg',res.data.data.url)
  })
})
const bg = localStorage.getItem('bg') || ''
document.body.style.backgroundImage = `url(${bg})`
/* document.querySelector('.bg-ipt').addEventListener('change', e => {
  // 1. 选择图片上传,设置body背景
  console.log(e.target.files[0])
  const fd = new FormData()
  fd.append('img', e.target.files[0])
  axios({
    url: 'http://hmajax.itheima.net/api/uploadimg',
    method: 'POST',
    data: fd
  }).then(result => {
    const imgUrl = result.data.data.url
    document.body.style.backgroundImage = `url(${imgUrl})`

    // 2. 上传成功时,"保存"图片url网址
    localStorage.setItem('bgImg', imgUrl)
  })
})

// 3. 网页运行后,"获取"url网址使用
const bgUrl = localStorage.getItem('bgImg')
console.log(bgUrl)
bgUrl && (document.body.style.backgroundImage = `url(${bgUrl})`) */

小结

  1. localStorage 取值和赋值的语法分别是什么?

    ​答案​ : localStorage.getItem('key')是取值,localStorage.setItem('key', 'value')是赋值

13.案例_个人信息设置-介绍

目标

  • 介绍个人信息设置案例-需要完成哪些效果,分几个视频讲解

讲解

  1. 先运行备课代码,查看要完成的效果

  2. 本视频分为,头像修改和信息修改2部分

    1. 先完成信息回显

    2. 再做头像修改-立刻就更新给此用户

    3. 收集个人信息表单-提交保存

    4. 提交后反馈结果给用户

代码区实现:

css样式

body {
  background-color: #f0f2f5;
  padding: 20px;
  -moz-user-select: none;
  /*火狐*/
  -webkit-user-select: none;
  /*webkit浏览器*/
  -ms-user-select: none;
  /*IE10*/
  -khtml-user-select: none;
  /*早期浏览器*/
  user-select: none;
  font-size: 14px;
}

.container {
  background-color: #ffffff;
  padding: 20px;
  display: flex;
  padding-left: 0;
  margin: 0 auto;
  min-width: 700px;
  max-width: 1000px;
  border-radius: 2px;
}

.my-nav {
  width: 200px;
  border-right: 1px solid #f0f0f0;
  list-style: none;
}

.my-nav li {
  cursor: pointer;
  height: 40px;
  line-height: 40px;
  padding-left: 20px;
  font-size: 14px;
}

.my-nav li.active {
  background-color: #e9f7fe;
  color: #448ef7;
  border-right: 4px solid #448ef7;
  font-weight: 600;
}

.content {
  padding-top: 10px;
  /* padding-left: 40px; */
  flex: 1;
  display: flex;
  justify-content: space-evenly;
}

.content .title {
  font-size: 20px;
  margin-bottom: 30px;
}

.content .info-wrap {
  /* margin-right: 20px; */
}

.content .avatar-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 20px;
  /* flex: 1; */
  padding-top: 55px;
  width: 200px;
}

.content .avatar-box .avatar-title {
  font-size: 16px;
  text-align: left;
  align-self: flex-start;
  margin: 0;
}

.content .avatar-box .prew {
  width: 120px;
  height: 120px;
  border-radius: 50%;
  margin-bottom: 15px;
}

.content .avatar-box label {
  width: 100px;
  height: 30px;
  transition: all .3s;
  box-shadow: 0 2px 0 rgb(0 0 0 / 2%);
  cursor: pointer;
  font-size: 14px;
  border-radius: 2px;
  color: rgba(0, 0, 0, .85);
  border: 1px solid #d9d9d9;
  text-align: center;
  line-height: 30px;
}

.content .avatar-box label:hover {
  color: #40a9ff;
  border-color: #40a9ff;
  background: #fff;
}

.content .avatar-box #upload {
  display: none;
}



.content .user-form label:not(.male-label) {
  display: block;
  margin-bottom: 15px;
  margin-top: 15px;
}

.content .user-form .form-item {
  margin-bottom: 20px;
}

.content .user-form .form-item .male-label {
  margin-right: 20px;
  display: inline-flex;
  align-items: center;
}

.content .user-form input[type=radio] {
  margin-right: 10px;
  outline: none;
  background-size: contain;
  border: 1px solid rgba(0, 0, 0, .25);
  border-radius: 50%;
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  appearance: none;

}



.content .user-form input[type=radio]:checked {
  border-radius: 50%;
  border-color: #0d6efd;
  background-color: #0d6efd;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e");
}



.content .user-form input:not([type=radio]) {
  height: 30px;
  appearance: none;
}


.form-item input:not([type=radio]),
textarea {
  display: block;
  width: 330px;
  outline: none;
  font-size: 14px;
  border: 1px solid #d9d9d9;
  border-radius: 2px;
  transition: all .3s;
  padding-left: 5px;
}

.content .user-form textarea {
  padding-top: 10px;
  width: 350px;
  resize: none;
}



.content .user-form input:focus,
textarea:focus {
  border-color: #40a9ff;
  border-right-width: 1px;
  z-index: 1;
  border-color: #40a9ff;
  box-shadow: 0 0 0 2px rgb(24 144 255 / 20%);
  border-right-width: 1px;
  outline: 0;
}

.content .user-form button.submit {
  background: #4D8FF7;
  width: 78px;
  height: 32px;
  color: white;
  text-align: center;
  line-height: 32px;
  font-size: 14px;
  color: #FFFFFF;
  letter-spacing: 0;
  font-weight: 500;
  border: none;
  cursor: pointer;
}

.toast {
  position: fixed;
  top: 20px;
  left: 50%;
  transform: translateX(-50%);
}

.toast .toast-body {
  padding: 0 !important;
}

.toast .alert-success {
  margin-bottom: 0 !important;
}

同上引入form-serialize.js文件

index.html

<!DOCTYPE html>
<html lang="zh-CN">

<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 href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
  <!-- 核心样式 -->
  <link rel="stylesheet" href="./css/index.css">
  <title>个人信息设置</title>
</head>

<body>
  <!-- toast 提示框 -->
  <div class="toast my-toast" data-bs-delay="1500">
    <div class="toast-body">
      <div class="alert alert-success info-box">
        操作成功
      </div>
    </div>
  </div>
  <!-- 核心内容区域 -->
  <div class="container">
    <ul class="my-nav">
      <li class="active">基本设置</li>
      <li>安全设置</li>
      <li>账号绑定</li>
      <li>新消息通知</li>
    </ul>
    <div class="content">
      <div class="info-wrap">
        <h3 class="title">基本设置</h3>
        <form class="user-form" action="javascript:;">
          <div class="form-item">
            <label for="email">邮箱</label>
            <input id="email" name="email" class="email" type="text" placeholder="请输入邮箱" autocomplete="off">
          </div>
          <div class="form-item">
            <label for="nickname">昵称</label>
            <input id="nickname" name="nickname" class="nickname" type="text" placeholder="请输入昵称" autocomplete="off">
          </div>
          <div class="form-item">
            <label>性别</label>
            <label class="male-label"><input type="radio" name="gender" class="gender" value="0">男</label>
            <label class="male-label"><input type="radio" name="gender" class="gender" value="1">女</label>
          </div>
          <div class="form-item">
            <label for="desc">个人简介</label>
            <textarea id="desc" name="desc" class="desc" placeholder="请输入个人简介" cols="20" rows="10" autocomplete="off"></textarea>
          </div>
          <button class="submit">提交</button>
        </form>
      </div>
      <div class="avatar-box">
        <h4 class="avatar-title">头像</h3>
          <img class="prew" src="./img/头像.png" alt="">
          <label for="upload">更换头像</label>
          <input id="upload" type="file" class="upload">
      </div>

    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.min.js"></script>
  <script src="./lib/form-serialize.js"></script>
  <!-- 核心逻辑 -->
  <script src="./js/index.js"></script>
</body>

</html>

14.案例_个人信息设置-信息渲染

目标

  • 把外号对应的用户信息渲染到页面上

讲解

  1. 还是需要准备一个外号,因为想要查看自己对应的用户信息,不想被别人影响

  2. 实现个人信息渲染-回显到页面步骤:

    • 获取数据

    • 渲染数据到页面

代码:

/**
 * 目标1:信息渲染
 *  1.1 获取用户的数据
 *  1.2 回显数据到标签上
 * */ 
// 获取数据
const creator = '小聪'
axios({
  url:'http://hmajax.itheima.net/api/settings',
  params:{
     creator
  }
}).then(res=>{
    const data = res.data.data
    // 回显数据到标签上
    Object.keys(data).forEach(k =>{
      if (k === 'avator') {
        document.querySelector('.prew').src = data[k]
      }else if(k === 'gender'){
        const genders = document.querySelectorAll('.gender')
        const n = data[k]
        genders[n].checked = true
      }else{
        document.querySelector(`.${k}`).value = data[k]

      }
    })
})

小结

  1. 渲染数据和图书列表的渲染思路是否一样呢,是什么?

    ​答案 : 一样的,都是获取到数据,然后渲染到页面上

15.案例_个人信息设置-头像修改

目标

  • 修改用户的头像并立刻生效

讲解

  1. 实现步骤如下:

    1. 获取到用户选择的头像文件

    2. 调用头像修改接口,并除了头像文件外,还要在 FormData 表单数据对象中携带外号

    3. 提交到服务器保存此用户对应头像文件,并把返回的头像图片 url 网址设置在页面上

  2. 注意:重新刷新重新获取,已经是修改后的头像了(证明服务器那边确实保存成功)

代码:

/**
 * 目标2:修改头像
 *  2.1 获取头像文件
 *  2.2 提交服务器并更新头像
 * */
// 文件选择元素->change事件
document.querySelector('.upload').addEventListener('change',e =>{
  const data = new FormData()
  data.append('avator',e.target.files[0])
  data.append('creator',creator)
  axios({
    url:`http://hmajax.itheima.net/api/avatar`,
    method:'PUT',
    data
  }).then(res=>{
    const n = res.data.data.avatar
    document.querySelector('.prew').src = n
  })
})

小结

  1. 为什么这次上传头像,需要携带外号呢?

    ​答案​ : 因为这次头像到后端,是要保存在某个用户名下的,所以要把外号名字一起携带过去

16.案例_个人信息设置-信息修改

目标

  • 把用户修改的信息提交到服务器保存

讲解

  1. 类似编辑实现的思路,只不过表单标签准备好了,而且数据已经在页面

    1. 收集表单数据

    2. 提交到服务器保存-调用用户信息更新接口(注意请求方法是 PUT)代表数据更新的意思

代码:

// 保存修改->点击
document.querySelector('.submit').addEventListener('click', () => {
  // 3.1 收集表单信息
  const userForm = document.querySelector('.user-form')
  const userObj = serialize(userForm, { hash: true, empty: true })
  userObj.creator = creator
  // 性别数字字符串,转成数字类型
  userObj.gender = +userObj.gender
  console.log(userObj)
  // 3.2 提交到服务器保存
  axios({
    url: 'http://hmajax.itheima.net/api/settings',
    method: 'PUT',
    data: userObj
  }).then(result => {
    // 4.1 创建toast对象
    const toastDom = document.querySelector('.my-toast')
    const toast = new bootstrap.Toast(toastDom)

    // 4.2 调用show方法->显示提示框
    toast.show()
  })
})

小结

  1. 信息修改数据和以前增删改查哪个实现的思路比较接近呢?

    ​答案​ : 编辑,首先回显已经做完了,然后收集用户最新改动后的数据,提交到服务器保存,因为页面最终就是用户刚写的数据,所以不用重新获取并刷新页面了

17.案例_个人信息设置-提示框

目标

  • 把用户更新个人信息结果,用提示框反馈给用户

讲解

  1. 这里准备好 bootstrap 提示框和内容

  2. 在提交成功的 axios 回调函数中,用 JS 的方式,展示 bootstrap 提示框告知用户更新成功

小结

  1. bootstrap 弹框什么时候用 JS 方式控制显示呢?

    ​答案​ : 需要执行一些其他的 JS 逻辑后,再去显示/隐藏弹框时

今日重点(必须会)

  1. 掌握增删改查数据的思路

  2. 掌握图片上传的思路和流程

  3. 理解调用接口时,携带外号的作用

  4. 了解 bootstrap 弹框的使用

参考文献

  1. 表单概念->百度百科

  2. accept属性->mdn

  3. accept属性->菜鸟教程

  4. FormData->mdn

  5. BS的Model文档

  6. axios请求方式别名

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值