【AJAX】图书管理系统

发布于:2025-07-24 ⋅ 阅读:(36) ⋅ 点赞:(0)

目录

一、BootStrap 弹窗

二、 图书管理 - 渲染列表

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

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

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

4. 总结一句话:

额外举个通俗例子

新增图书

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

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

收集表单插件 —— serialize插件

删除图书

编辑图书

点击修改按钮

三、图片上传

四、更换背景

五、 个人信息设置

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


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

一、BootStrap 弹窗

1. 通过属性的方式来控制弹框的显示或隐藏

  •  引入bootstrap.css 和 bootstrap.js
  <!-- 引入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

  <!-- 
    弹框标签
    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

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

关闭弹窗属性

data-bs-dismiss="modal"

2. js控制弹窗的显示和隐藏

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

    // 创建弹框对象
    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

二、 图书管理 - 渲染列表

数据请求+渲染:

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操作流程!!!

新增图书

// 创建一个弹框对象
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插件

  // 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: '武汉传媒学院'}

删除图书

// 删除图书
// 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的形式来进行删除

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

注意:

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

e.target.classList.contains('del')

编辑图书

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

document.querySelector('.edit').addEventListener('click', () => {

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

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

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

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

    // 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'来进行修改者的输入

  // 进行解构
  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()

// 目标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携带图片文件
  3. const fd = new FormData()
    fd.append(参数名, 值)
  4. 提交表单数据到服务器, 使用图片url网址

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

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

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

const data = {
  img: e.target.files[0]
}

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

axios.post(url, data)

  <!-- 文件选择元素 -->
  <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 模拟表单,能包含文本和二进制数据 ✅ 能上传文件
普通对象 {} 只能传递普通文本数据 ❌ 不行
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就直接更新背景,没有就为空,也不会报错了

/**
 * 目标:网站-更换背景
 *  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的

// 获取用户信息
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

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提交表单数据到服务器

// 提交表单
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)
})

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


网站公告

今日签到

点亮在社区的每一天
去签到