【AJAX】图书管理系统

目录

[一、BootStrap 弹窗](#一、BootStrap 弹窗)

[二、 图书管理 - 渲染列表](#二、 图书管理 - 渲染列表)

[1. params 是用来给服务器接口 传递查询参数 的](#1. params 是用来给服务器接口 传递查询参数 的)

[2. 为什么你以前读 JSON 文件不需要?](#2. 为什么你以前读 JSON 文件不需要?)

[3. 但这里请求的是 服务器接口](#3. 但这里请求的是 服务器接口)

[4. 总结一句话:](#4. 总结一句话:)

额外举个通俗例子

新增图书

[GET 请求:用来获取数据,数据通过 URL 的参数(例如 ?creator=老张)传给服务器,一般用于"读"操作,比如获取书籍列表、文章内容、用户信息等](#GET 请求:用来获取数据,数据通过 URL 的参数(例如 ?creator=老张)传给服务器,一般用于“读”操作,比如获取书籍列表、文章内容、用户信息等)

[POST 请求:用来提交数据(如:新增、注册、提交表单等),数据通过 请求体(body) 传递,不会暴露在地址栏里,一般用于"写"操作,比如:新增图书、用户注册、发表评论等。](#POST 请求:用来提交数据(如:新增、注册、提交表单等),数据通过 请求体(body) 传递,不会暴露在地址栏里,一般用于“写”操作,比如:新增图书、用户注册、发表评论等。)

[收集表单插件 ------ serialize插件](#收集表单插件 —— serialize插件)

删除图书

编辑图书

点击修改按钮

三、图片上传

四、更换背景

[五、 个人信息设置](#五、 个人信息设置)

[总结不易~ 本章节对我有很大的收获, 希望对你也是!!!](#总结不易~ 本章节对我有很大的收获, 希望对你也是!!!)


本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study

一、BootStrap 弹窗

  1. 通过属性的方式来控制弹框的显示或隐藏
  • 引入bootstrap.css 和 bootstrap.js
html 复制代码
  <!-- 引入bootstrap.css -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet">

  <!-- 引入bootstrap.js -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.min.js"></script>
  • 准备弹框标签,确认结构

Bootstrap(modal):Modal · Bootstrap v5.3

html 复制代码
  <!-- 
    弹框标签
    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>
  • 给点击按钮,通过自定义属性,控制弹框的显示和隐藏

class="btn btn-primary" data-bs-toggle="modal" 时bootstrap js库给我们提供的

由于一个html可能存在多个弹框,所以我们要给该弹框绑定自定义属性data-bs-target

html 复制代码
  <button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target=".my-box">
    显示弹框
  </button>

关闭弹窗属性

html 复制代码
data-bs-dismiss="modal"
  1. js控制弹窗的显示和隐藏

就是简单的js对象方法调用 学了vue之后 这些都是小菜j

javascript 复制代码
    // 创建弹框对象
    const modalDom = document.querySelector('.name-box')
    const modal = new bootstrap.Modal(modalDom)

    // 编辑姓名-> 点击 -> 弹框显示
    document.querySelector('.btn-primary').addEventListener('click', () => {
      // 2. 显示弹框
      modal.show()
    })

    document.querySelector('.save-btn').addEventListener('click', () => {
      // 2. 点击保存隐藏弹框
      modal.hide()
    })

总结:

  • 如果再关闭弹窗的时候,是想要直接关闭,就可以用属性的方式更加方便一点;
  • 如果是再关闭之前有一些逻辑要进行执行(比如:返回数据,获取数据)就是js方法进行显示或隐藏更好;

本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study

二、 图书管理 - 渲染列表

数据请求+渲染:

javascript 复制代码
const demo = async () => {
  // 1.1 获取数据
  const reperson = await axios.get('http://hmajax.itheima.net/api/books', {
    params: {
      // 外号: 获取对应数据
      creator: 'hha'
    }
  })
  const bookList = reperson.data.data
  console.log(bookList)

  // 1.2 渲染数据
  const htmlStr = bookList.map((item, index) => {
    return `
      <tr>
        <td>${index}</td>
        <td>${item.bookname}</td>
        <td>${item.author}</td>
        <td>${item.publisher}</td>
        <td>
          <span class="del">删除</span>
          <span class="edit">编辑</span>
        </td>
      </tr>
    `
  }).join('')

  console.log(htmlStr)
  document.querySelector('.list').innerHTML = htmlStr

}

demo()

1. params 是用来给服务器接口 传递查询参数

  • 它对应的是 HTTP GET 请求里的 URL 查询字符串(query string),形如:

    http://hmajax.itheima.net/api/books?creator=老张

  • axios 里你写:

    复制代码
    params: { creator: '老张' } 

    它帮你自动把这个对象转成 ?creator=老张 拼接到 URL 后面。


2. 为什么你以前读 JSON 文件不需要?

  • 你以前可能是直接访问本地静态的 .json 文件,比如:

    axios.get('/data/books.json')

  • 这是直接读静态文件,不需要任何额外参数,服务器就直接给你完整数据。


3. 但这里请求的是 服务器接口

  • 这个接口会根据你传递的参数(如 creator)返回不同的数据;

  • 如果你不给它参数,服务器会拒绝,返回 400 错;

  • 所以你必须通过 params 明确告诉服务器你要查谁的书。


4. 总结一句话:

params 就是告诉服务器"我请求的这个接口,请根据这些条件帮我筛选数据",而你直接读 JSON 文件则不需要,因为那是静态资源,没筛选。


额外举个通俗例子

  • 你去餐厅点餐:

    • JSON 文件相当于菜单,你拿菜单看内容,不需要额外说明;

    • 接口请求相当于你点菜,你得告诉服务员"我要一份宫保鸡丁",这"宫保鸡丁"就是参数。

点击添加要弹出bootStrap-modal弹窗!

给当前按钮添加一个自定义属性,绑定后进行显示。

点击取消可以直接隐藏弹框,但是点击保存就是要将填入的数据进行存入再后台,然后再次往页面进行渲染!所以点击保存,要走js操作流程!!!

新增图书

javascript 复制代码
// 创建一个弹框对象
const addModalDom = document.querySelector('.add-modal')
const addModal = new bootstrap.Modal(addModalDom)
document.querySelector('.add-btn').addEventListener('click', async () => {
  // 收集表单,并提交到服务器
  const addForm = document.querySelector('.add-form')
  // serialize插件 它能把 HTML 表单序列化成对象或字符串,非常方便。
  // hash:true -> "name=小智&age=20" 转换成 { name: "小智", age: "20" }
  // empty: true -> 加了 empty: true,即使表单项为空,也会把该键以空字符串 "" 的形式序列化进去。
  const bookObj = serialize(addForm, { hash: true, empty: true })
  console.log(bookObj) // {bookname: '哈哈', author: '流哈哈', publisher: '武汉传媒学院'}

  // 提交到服务器
  // axios({
  //   url: 'http://hmajax.itheima.net/api/books',
  //   method: 'POST',
  //   data: {
  //     ...bookObj,
  //     creator: '老张'
  //   }
  // }).then(result => {
  //   console.log(result)
  //   // 2.3 添加成功后, 重新请求并渲染图书列表
  //   getBooksList()
  // })

  const response = await axios.post('http://hmajax.itheima.net/api/books', {
    ...bookObj,
    creator: 'hha'
  })
  console.log(response)
  await getBooksList()


  addModal.hide()
})

里面有两点:

post是什么?和get有什么区别?

GET 请求:用来获取数据, 数据通过 URL 的参数 (例如 ?creator=老张)传给服务器,一般用于"读"操作,比如获取书籍列表、文章内容、用户信息等

POST 请求:用来提交数据 (如:新增、注册、提交表单等),数据通过 请求体(body) 传递,不会暴露在地址栏里,一般用于"写"操作,比如:新增图书、用户注册、发表评论等。

二者都要传递参数,creator:'hha'

  • axios.post(url, data),第二个参数是请求体 data,直接把对象发给服务器。

  • axios.get(url, config),第二个参数是配置对象,不能直接放数据,参数必须写在 config.params 里。

因为 HTTP 协议规定:GET 请求没有请求体,数据只能放 URL 参数里,这也是 RESTful 设计规范的基本要求。
creator相当于post中是谁进行提交到的,get中是要获取谁提交的数据,所以get和post二者的creator要一致!!!

收集表单插件 ------ serialize插件

javascript 复制代码
  // serialize插件 它能把 HTML 表单序列化成对象或字符串,非常方便。
  // hash:true -> "name=小智&age=20" 转换成 { name: "小智", age: "20" }
  // empty: true -> 加了 empty: true,即使表单项为空,也会把该键以空字符串 "" 的形式序列化进去。
  const bookObj = serialize(addForm, { hash: true, empty: true })
  console.log(bookObj) // {bookname: '哈哈', author: '流哈哈', publisher: '武汉传媒学院'}

删除图书

javascript 复制代码
// 删除图书
// 3.1 删除元素 -> (事件委托)
document.querySelector('.list').addEventListener('click', e => {
  // 获取触发事件目标元素
  // console.log(e.target)
  // 判断点击的是删除元素 contains常用来判断一个元素的某个 class 类名 是否存在。
  if (e.target.classList.contains('del')) {
    console.log('点击删除元素')
    // 获取图书的id
    // parentNode 上面一个父节点 
    // closest('.') 指定父节点
    const id = e.target.dataset.id
    const demo = async () => {
      await axios.delete(`http://hmajax.itheima.net/api/books/${id}`)
      await getBooksList()
    }
    demo()

  }
})

这里我真的蠢死了,我开始没有给html删除标签打上data-id,然后我就想通过过滤来覆盖服务器里面的数组,结果我没有清空,内容就越来越多。可是清空就必须要得到id,我就没办法,我还是又axios.get()来得到一遍数据,转换成数组来得到我点击的是哪个元素来得到里面的id,这里真的蠢死了,其实只要给删除按钮打上data-id就可以了直接获得id了。

删除语法:是通过路径后面 + id的形式来进行删除

javascript 复制代码
    const demo = async () => {
      await axios.delete(`http://hmajax.itheima.net/api/books/${id}`)
      await getBooksList()
    }
    demo()

注意:

contains是类名的操作方法,所以都是在classList里面存在的~ 判断是否存在当前类名

javascript 复制代码
e.target.classList.contains('del')

编辑图书

我最开始直接想要获取点击编辑按钮的DOM元素,可是失败了

javascript 复制代码
document.querySelector('.edit').addEventListener('click', () => {

就是因为edit是存在多个页面标签,存在多个就需要用到 querySelectorAll,所以如果直接获取edit元素就要用querySelectorAll.forEach()来进行遍历,当前点击的是哪个edit元素

那么就还是对父节点list进行查找e.target.classList.contains('edit')当前的点击元素比较方便

将浏览器数据进行渲染到编辑框中

首先获取浏览器的数据,对他进行数据解构,利用Object.keys(),能将bookObj的键存成一个数组,在对该数组进行遍历来修改DOM元素节点的值,这里要注意遍历的时候,key是每个元素,点语法 .key 是访问字面量属性名,方括号语法 [key] 才是访问变量代表的属性名。

javascript 复制代码
    // axios来获取详细数据
    const response = await axios.get(`http://hmajax.itheima.net/api/books/${theId}`)
    const bookObj = response.data.data
    // document.querySelector('.edit-form .bookname').value = bookObj.bookname
    // document.querySelector('.edit-form .author').value = bookObj.author
    // document.querySelector('.edit-form .publisher').value = bookObj.publisher

    // 数据对象"属性" 和 标签"类名"一致
    // 遍历数据对象, 使用属性去获得对应的标签, 快速赋值
    const keys = Object.keys(bookObj) // ['id', 'bookname', 'author', 'publisher']
    keys.forEach(key => {
      // 通过变量名 动态 访问对象属性,必须用中括号 [],不能用点语法。
      document.querySelector(`.edit-form .${key}`).value = bookObj[key]
    })

点击修改按钮

要进行数据修改,就是对该页面修改完成后,重新获取form表单数据,然后重新返回服务器,然后再次渲染页面,这里就用上了axios put提交信息语法,直接在链接后面跟上data数据{},同时要跟上creator:'hha'来进行修改者的输入

javascript 复制代码
  // 进行解构
  const { id, bookname, author, publisher } = serialize(editForm, { hash: true, empty: true })

  const response = await axios.put(`http://hmajax.itheima.net/api/books/${id}`, {
    bookname,
    author,
    publisher,
    creator: 'hha'
  })

  // 修改成功 重新渲染列表
  getBooksList()
javascript 复制代码
// 目标4 编辑图书
const editDom = document.querySelector('.edit-modal')
const editModal = new bootstrap.Modal(editDom)
document.querySelector('.list').addEventListener('click', async e => {
  // 为什么非要这么写呢 就是因为页面存在多个edit标签, 你就必须要用querySelectorAll.forEach()来进行遍历, 那么这种查找就不需要进行遍历
  if (e.target.classList.contains('edit')) {
    // console.log('一定是编辑')
    editModal.show()
    // console.log('e', e.target)
    // 点击编辑就要拿到测试的id 原生js找任意兄弟节点属性
    const theId = e.target.parentNode.querySelector('.del').dataset.id
    console.log(theId)
    // axios来获取详细数据
    const response = await axios.get(`http://hmajax.itheima.net/api/books/${theId}`)
    const bookObj = response.data.data
    // document.querySelector('.edit-form .bookname').value = bookObj.bookname
    // document.querySelector('.edit-form .author').value = bookObj.author
    // document.querySelector('.edit-form .publisher').value = bookObj.publisher

    // 数据对象"属性" 和 标签"类名"一致
    // 遍历数据对象, 使用属性去获得对应的标签, 快速赋值
    const keys = Object.keys(bookObj) // ['id', 'bookname', 'author', 'publisher']
    keys.forEach(key => {
      // 通过变量名 动态 访问对象属性,必须用中括号 [],不能用点语法。
      document.querySelector(`.edit-form .${key}`).value = bookObj[key]
    })
  }
})

// 点击修改按钮
document.querySelector('.edit-btn').addEventListener('click', async () => {
  // 提交保存修改 并刷新列表
  const editForm = document.querySelector('.edit-form')
  // 进行解构
  const { id, bookname, author, publisher } = serialize(editForm, { hash: true, empty: true })

  const response = await axios.put(`http://hmajax.itheima.net/api/books/${id}`, {
    bookname,
    author,
    publisher,
    creator: 'hha'
  })

  // 修改成功 重新渲染列表
  getBooksList()

  editModal.hide()
})

整体来说,这一节就是学习了axios与服务器交互的各种语法

方法 用途 请求语法 请求数据位置 示例
get 获取数据(查询) axios.get(url, { params }) 查询参数在 URL 中(params 会拼接) axios.get('/api/books', { params: { page: 1 } })
post 新增数据 axios.post(url, data) 数据在请求体中(body axios.post('/api/books', { title: '书名' })
put 修改全部字段 axios.put(url, data) 数据在请求体中(更新整条记录) axios.put('/api/books/1', { title: '新书名', author: '作者' })
patch 修改部分字段 axios.patch(url, data) 数据在请求体中(只传改动字段) axios.patch('/api/books/1', { author: '新作者' })
delete 删除数据 axios.delete(url)axios.delete(url, { data }) 通常 ID 写在 URL,复杂情况可带 body axios.delete('/api/books/1')

本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study

三、图片上传

  1. 获取图片文件对象
  2. 使用FormData携带图片文件
javascript 复制代码
const fd = new FormData()
fd.append(参数名, 值)
  1. 提交表单数据到服务器, 使用图片url网址

FormData浏览器内置的一个对象,用于构造表单数据,可以方便地用于 POST 请求中,特别是文件上传。

往服务器里面提交图片为什么非要是FormData浏览器内置对象呢?

因为普通的 JSON 对象是不能包含文件(二进制数据)的。如果你用这种方式写:

javascript 复制代码
const data = {
  img: e.target.files[0]
}

然后发送 JSON 请求:会失败,服务器接收不到上传的图片。

javascript 复制代码
axios.post(url, data)
html 复制代码
  <!-- 文件选择元素 -->
  <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', async e => {
      // 1. 获取图片文件
      console.log(e.target.files[0])
      // 2. 使用FormData 携带图片文件
      const fd = new FormData()
      fd.append('img', e.target.files[0])
      // 3. 提交到服务器 获取图片url网址使用
      const response = await axios.post('http://hmajax.itheima.net/api/uploadimg', fd)
      console.log(response.data.data.url)
      const imgUrl = response.data.data.url
      document.querySelector('.my-img').src = imgUrl
    })

  </script>

总结

对象 用途 是否能上传文件
FormData 模拟表单,能包含文本和二进制数据 ✅ 能上传文件
普通对象 {} 只能传递普通文本数据 ❌ 不行
javascript 复制代码
const fd = new FormData();               // 创建表单数据
fd.append('img', e.target.files[0]);     // 把用户选的文件加进去
await axios.post(url, fd);               // 发 POST 请求上传

这是文件上传的标准写法,没得简化,必须用 FormData

本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study

四、更换背景

label for 用来关联 id 的值,点到 label 上就跟点击 input 是一个道理

点击更换按钮后,将图片的url地址进行上传至服务器,然后保存到本地,每次页面刷新就先到本地进行查找,如果本地有图片的url就直接更新背景,没有就为空,也不会报错了

javascript 复制代码
/**
 * 目标:网站-更换背景
 *  1. 选择图片上传,设置body背景
 *  2. 上传成功时,"保存"图片url网址
 *  3. 网页运行后,"获取"url网址使用
 * */

document.querySelector('.bg-ipt').addEventListener('change', async e => {
  // console.log(e.target.files[0])
  // 上传图片到服务器 要求上传 表单数据
  // FormData 是前端用来模拟表单(尤其是含文件)的数据结构
  const fd = new FormData()
  fd.append('img', e.target.files[0])
  const response = await axios.post('http://hmajax.itheima.net/api/uploadimg', fd)
  const imgUrl = response.data.data.url
  document.body.style.backgroundImage = `url(${response.data.data.url})`

  // 2. 上传成功 要保存到本地 
  localStorage.setItem('bgImg', imgUrl)
})


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

本节素材已上传至Gitee:https://gitee.com/liu-yihao-hhh/ajax_studyhttps://gitee.com/liu-yihao-hhh/ajax_study

五、 个人信息设置

用户信息获取,这是服务器里面存在的表单数据

但是不是所有DOM元素都是通过.value的形式来进行直接赋值就可以的,比如头像更换和性别的单选框,都是要进行分别单独设置src 和 checked的

javascript 复制代码
// 获取用户信息
const demo = async () => {
  const response = await axios.get('http://hmajax.itheima.net/api/settings', {
    // params 是专门用在 axios.get() 请求中的,它会自动把你提供的对象转成 URL 的查询字符串
    params: {
      creator: 'hha'
    }
  })
  const userObj = response.data.data
  console.log(userObj)
  Object.keys(userObj).forEach(key => {
    if (key === 'avatar') document.querySelector('.prew').src = userObj[key]
    else if (key === 'gender') {
      // 性别
      const gRadioList = document.querySelectorAll('.gender')
      gRadioList[userObj[key]].checked = true
    }
    else document.querySelector(`.${key}`).value = userObj[key]
  })
}
demo()

头像修改

道理跟上一个案例一模一样,也是提交FormData表单数据到服务器,然后重新添加图片url(avatar属性名) 和 传送人creator 然后提交到服务器put 再通过服务器的数据进行改变本地DOM元素节点的src

javascript 复制代码
document.querySelector('.upload').addEventListener('change', async e => {
  console.log(e.target.files[0])
  // 提交表单数据
  const fd = new FormData()
  fd.append('avatar', e.target.files[0])
  fd.append('creator', creator = 'hha')
  // 提交到服务器
  const response = await axios.put('http://hmajax.itheima.net/api/avatar', fd)
  console.log(response.data)
  document.querySelector('.prew').src = response.data.data.avatar
})

提交表单

利用seialize插件来讲form表单数据json化,然后进行put提交表单数据到服务器

javascript 复制代码
// 提交表单
document.querySelector('.submit').addEventListener('click', async () => {
  const userForm = document.querySelector('.user-form')
  // serialize 表单插件插件 要在(1. from表单下, 2. DOM标签存在name属性的值)
  const userObj = serialize(userForm, { hash: true, empty: true })
  // console.log(userObj) // email: 'itheima@itcast.cn', nickname: 'itheima', gender: '1', desc: '我是hha'

  // 提交到服务器
  userObj.gender = +userObj.gender
  console.log(userObj)
  const response = await axios.put('http://hmajax.itheima.net/api/settings', userObj)
})

总结不易~ 本章节对我有很大的收获, 希望对你也是!!!

相关推荐
@大迁世界8 分钟前
TypeScript 的本质并非类型,而是信任
开发语言·前端·javascript·typescript·ecmascript
GIS之路16 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug20 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213822 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中43 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路1 小时前
GDAL 实现矢量合并
前端
hxjhnct1 小时前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星1 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全