当前位置:   article > 正文

AJAX——黑马头条-数据管理平台项目

AJAX——黑马头条-数据管理平台项目

1.项目介绍

功能:

  1. 登录和权限判断
  2. 查看文章内容列表(筛选,分页)
  3. 编辑文章(数据回显)
  4. 删除文章
  5. 发布文章(图片上传,富文本编辑器)

2.项目准备

技术:

  • 基于Bootstrap搭建网站标签和样式
  • 集成wangEditor插件实现富文本编辑器
  • 使用原生JS完成增删改查等业务
  • 基于axios与黑马头条线上接口交互
  • 使用axios拦截器进行权限判断
  • 准备配套的素材代码
  • 包含:html、css、js,静态图片,第三方插件等等

目录管理:建议这样管理,方便查找

  • assets:资源文件夹(图片,字体等)
  • lib:资料文件夹(第三方插件,例如:form-serialize)
  • page:页面文件夹
  • utils:实用程序文件夹(工具插件)

3.验证码登录

目标:完成验证码登录,后端设置验证码默认为246810

原因:因为短袖接口不是免费的,防止攻击者恶意盗刷

步骤:

  1. 在utils/request.js配置axios请求基地址
    1. 作用:提取公共前缀地址,配置后axios请求时都会 baseURL+ url
      1. // axios 公共配置
      2. // 基地址
      3. axios.defaults.baseURL = 'http://geek.itheima.net'
  2. 收集手机号和验证码数据
  3. 基于axios调用验证码登录接口
  4. 使用Bootstrap的Alert警告框反馈结果给用户

 index.js

  1. /**
  2. * 目标1:验证码登录
  3. * 1.1 在 utils/request.js 配置 axios 请求基地址
  4. * 1.2 收集手机号和验证码数据
  5. * 1.3 基于 axios 调用验证码登录接口
  6. * 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户
  7. */
  8. // 1.2 收集手机号和验证码数据
  9. document.querySelector('.btn').addEventListener('click', () => {
  10. const form = document.querySelector('.login-form')
  11. const data = serialize(form, { hash: true, empty: true})
  12. console.log(data)
  13. // 1.3 基于 axios 调用验证码登录接口
  14. axios({
  15. url: '/v1_0/authorizations',
  16. method: 'POST',
  17. data: data
  18. }).then( result => {
  19. // 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户
  20. myAlert(true, '登录成功')
  21. console.log(result)
  22. }).catch(error => {
  23. myAlert(false, error.response.data.message)
  24. console.dir(error.response.data.message)
  25. })
  26. })

验证码登录流程

4.token的介绍

概念:访问权限的令牌,本质上是一串字符串

创建:正确登录后,由后端签发并返回

作用:判断是否有登录状态等,控制访问权限

注意:前端只能判断token有无,而后端才能判断token的有效性

5.token的使用

目标:只有登录状态,才可以访问内容页面

步骤:

1.在utils/auth.js中判断无token令牌字符串,则强制跳转到登录页(手动修改地址栏测试)

2.在登录成功后,保存token令牌字符串到本地,再跳转到首页(手动修改地址栏测试)

  1. // 权限插件(引入到了除登录页面,以外的其他所有页面)
  2. /**
  3. * 目标1:访问权限控制
  4. * 1.1 判断无 token 令牌字符串,则强制跳转到登录页
  5. * 1.2 登录成功后,保存 token 令牌字符串到本地,并跳转到内容列表页面
  6. */
  7. // 1.1 判断无 token 令牌字符串,则强制跳转到登录页
  8. const token = localStorage.getItem('token')
  9. if (!token) {
  10. location.href = '../login/index.html'
  11. }
  1. /**
  2. * 目标1:验证码登录
  3. * 1.1 在 utils/request.js 配置 axios 请求基地址
  4. * 1.2 收集手机号和验证码数据
  5. * 1.3 基于 axios 调用验证码登录接口
  6. * 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户
  7. */
  8. // 1.2 收集手机号和验证码数据
  9. document.querySelector('.btn').addEventListener('click', () => {
  10. const form = document.querySelector('.login-form')
  11. const data = serialize(form, { hash: true, empty: true})
  12. console.log(data)
  13. // 1.3 基于 axios 调用验证码登录接口
  14. axios({
  15. url: '/v1_0/authorizations',
  16. method: 'POST',
  17. data: data
  18. }).then( result => {
  19. // 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户
  20. myAlert(true, '登录成功')
  21. console.log(result)
  22. // 登录成功后,保存 token 令牌字符串到本地,并跳转到内容列表页面
  23. localStorage.setItem('token', result.data.data.token)
  24. setTimeout(() => {
  25. // 延迟跳转,让alert警告框停留一会
  26. location.href = '../content/index.html'
  27. },1500)
  28. }).catch(error => {
  29. myAlert(false, error.response.data.message)
  30. console.dir(error.response.data.message)
  31. })
  32. })

token的作用?

  • 判断用户是否有登录状态等

token的注意:

  • 前端只能判断token的有无
  • 后端通过解密可以提取token字符串的原始信息,判断有效性

 6.个人信息设置和axios请求拦截器

需求:设置用户昵称

语法:axios可以在headers选项传递请求头参数

问题:很多接口,都需要携带token令牌字符串

解决:在请求拦截器统一设置公共headers选项

axios请求拦截器:发起请求之前,触发的配置函数,对请求参数进行额外配置

对应代码

  1. /**
  2. * 目标2:设置个人信息
  3. * 2.1 在 utils/request.js 设置请求拦截器,统一携带 token
  4. * 2.2 请求个人信息并设置到页面
  5. */
  6. // 2.2 请求个人信息并设置到页面
  7. axios({
  8. url: '/v1_0/user/profile'
  9. }).then(result => {
  10. console.log(result)
  11. const username = result.data.data.name
  12. document.querySelector('.nick-name').innerHTML = username
  13. })
  1. // 添加请求拦截器
  2. axios.interceptors.request.use(function (config) {
  3. // 在发送请求之前做些什么
  4. // 统一携带 token 令牌字符串在请求头上
  5. const token = localStorage.getItem('token')
  6. token && (config.headers.Authorization = `Bearer ${token}`)
  7. return config;
  8. }, function (error) {
  9. // 对请求错误做些什么
  10. return Promise.reject(error);
  11. })

总结

1.什么是axios请求拦截器?

发起请求之前,调用的一个函数,对请求参数进行设置

2.axios请求拦截器,什么时候使用?

有公共配置和设置时,统一设置在请求拦截器中

7.axios响应拦截器和身份验证失败 

axios响应拦截器:响应回到 then/catch 之前,触发的拦截函数,对响应结果统一处理

例如:身份验证失败,统一判断并做处理

  1. // 添加响应拦截器
  2. axios.interceptors.response.use(function (response) {
  3. // 2xx 范围内前状态码都会触发该函数
  4. // 对响应数据做点什么
  5. return response;
  6. },function (error) {
  7. // 超出 2xx 范围的状态码都会触发该函数
  8. // 对响应错误数做点什么,例如:统一对 401 身份验证失败错误做出处理
  9. console.dir(error)
  10. if (error?.response?.status === 401) {
  11. alert('身份验证失败,请重新登录')
  12. localStorage.clear()
  13. location.href = '../login/index.html'
  14. }
  15. return Promise.reject(error)
  16. })

总结

1.什么是axios响应拦截器?

响应回到then/catch之前,触发的拦截函数,对响应结果统一处理

2.axios响应拦截器,什么时候触发成功/失败的回调函数?

状态为2xx触发成功回调,其他则触发失败的回调函数

 8.优化-axios响应结果

目标:axios直接接收服务器返回的响应结果

讲解:其实就是在响应拦截器里,response.data把后台返回的数据直接取出来统一返回给所有使用这个axios函数的逻辑页面位置的 then 的形参上

好处:可以让逻辑页面少一层data就能拿到后端返回的真正数据对象

对应代码

  1. axios.interceptors.response.use(function (response) {
  2. // 2xx 范围内的状态码都会触发该函数。
  3. // 对响应数据做点什么,例如:直接返回服务器的响应结果对象
  4. const result = response.data
  5. return result
  6. }, function (error) {
  7. // 超出 2xx 范围的状态码都会触发该函数。
  8. // 对响应错误做点什么,例如:判断响应状态为 401 代表身份验证失败
  9. if (error?.response?.status === 401) {
  10. alert('登录状态过期,请重新登录')
  11. window.location.href = '../login/index.html'
  12. }
  13. return Promise.reject(error);
  14. })

9.发布文章-富文本编辑器

富文本:带样式,多格式的文本,在前端一般使用标签配合内联样式实现

富文本编辑器:用于编写富文本内容的容器

目标:发布文章页,富文本编辑器的集成

使用:wangEditor插件

步骤:参考文档

  1. 引入CSS定义样式
  2. 定义HTML结构
  3. 引入JS创建编辑器
  4. 监听内容改变,保存在隐藏文本域(便于后期收集)

对应代码

  1. // 富文本编辑器
  2. // 创建编辑器函数,创建工具栏函数
  3. const { createEditor, createToolbar } = window.wangEditor
  4. const editorConfig = {
  5. // 占位提示文字
  6. placeholder: '发布文章内容...',
  7. // 编辑器变化时回调函数
  8. onChange(editor) {
  9. // 获取富文本内容
  10. const html = editor.getHtml()
  11. console.log('editor content', html)
  12. // 也可以同步到 <textarea>
  13. // 为了后续快速收集整个表单内容做铺垫
  14. document.querySelector('.publish-content').value = html
  15. }
  16. }
  17. const editor = createEditor({
  18. // 创建位置
  19. selector: '#editor-container',
  20. // 默认内容
  21. html: '<p><br></p>',
  22. // 配置项
  23. config: editorConfig,
  24. // 配置集成模式(default 全部) (simple 简洁)
  25. mode: 'default', // or 'simple'
  26. })
  27. // 工具栏配置对象
  28. const toolbarConfig = {}
  29. // 创建工具栏
  30. const toolbar = createToolbar({
  31. // 为指定编辑器创建工具栏
  32. editor,
  33. // 工具栏创建的位置
  34. selector: '#toolbar-container',
  35. // 工具栏配置对象
  36. config: toolbarConfig,
  37. // 配置集成模式
  38. mode: 'default', // or 'simple'
  39. })

10.发布文章-频道列表

目标:展示频道列表,供用户选择

步骤:

  1. 获取频道列表数据
  2. 展示到下拉菜单中
  1. /**
  2. * 目标1:设置频道下拉菜单
  3. * 1.1 获取频道列表数据
  4. * 1.2 展示到下拉菜单中
  5. */
  6. // 1.1 获取频道列表数据
  7. async function setChannleList() {
  8. const res = await axios({
  9. url: '/v1_0/channels'
  10. })
  11. // 1.2 展示到下拉菜单中
  12. const htmlStr = `<option value="" selected="">请选择文章频道</option>` + res.data.channels.map(item => `<option value="${item.id}">${item.name}</option>`).join('')
  13. console.log(htmlStr)
  14. document.querySelector('.form-select').innerHTML = htmlStr
  15. }
  16. // 网页运行后,默认调用一次
  17. setChannleList()

11.发布文章-封面设置

目标:文章封面的设置

步骤:

  1. 准备标签结构和样式
  2. 选择文件并保存在FormData
  3. 单独上传图片并得到图片URL地址
  4. 回显并切换img标签展示(隐藏 + 号上传标签)

注意:图片地址临时存储在img标签上,并未和文章关联保存

  1. /**
  2. * 目标2:文章封面设置
  3. * 2.1 准备标签结构和样式
  4. * 2.2 选择文件并保存在 FormData
  5. * 2.3 单独上传图片并得到图片 URL 网址
  6. * 2.4 回显并切换 img 标签展示(隐藏 + 号上传标签)
  7. */
  8. // 2.2 选择文件并保存在 FormData
  9. document.querySelector('.img-file').addEventListener('change', async e => {
  10. const file = e.target.files[0]
  11. const fd = new FormData()
  12. fd.append('image', file)
  13. // 2.3 单独上传图片并得到图片 URL 网址
  14. const res = await axios({
  15. url: '/v1_0/upload',
  16. method: 'POST',
  17. data: fd
  18. })
  19. console.log(res)
  20. // 2.4 回显并切换 img 标签展示(隐藏 + 号上传标签)
  21. const imgUrl = res.data.url
  22. document.querySelector('.rounded').src = imgUrl
  23. document.querySelector('.rounded').classList.add('show')
  24. document.querySelector('.place').classList.add('hide')
  25. })
  26. // 优化:点击 img 可以重新切换封面
  27. // 思路: img 点击 => 用JS方式触发文件选择元素 click 事件方法
  28. document.querySelector('.rounded').addEventListener('click', () => {
  29. document.querySelector('.img-file').click()
  30. })

12.发布文章-收集并保存

目标:收集文章内容,并提交保存

步骤:

1.基于form-serialize插件收集表单数据对象

2.基于axios提交到服务器保存

3.调用Alert警告框反馈结果给用户

  1. /**
  2. * 目标3:发布文章保存
  3. * 3.1 基于 form-serialize 插件收集表单数据对象
  4. * 3.2 基于 axios 提交到服务器保存
  5. * 3.3 调用 Alert 警告框反馈结果给用户
  6. * 3.4 重置表单并跳转到列表页
  7. */
  8. // 3.1 基于 form-serialize 插件收集表单数据对象
  9. document.querySelector('.send').addEventListener('click', async e => {
  10. const form = document.querySelector('.art-form')
  11. const data = serialize(form, { hash: true, empty: true})
  12. console.log(data)
  13. // 发布文章的时候,不需要 id 属性,所以可以删除掉(id为了后续做编辑使用)
  14. delete data.id
  15. console.log(data)
  16. // 自己收集封面图片地址并保存到 data 对象中
  17. data.cover = {
  18. type: 1, // 封面类型
  19. images: [document.querySelector('.rounded').src] // 封面图书 URL网址
  20. }
  21. // 3.2 基于 axios 提交到服务器保存
  22. try {
  23. const res = await axios({
  24. url: '/v1_0/mp/articles',
  25. method:'POST',
  26. data: data
  27. })
  28. // 3.3 调用 Alert 警告框反馈结果给用户
  29. myAlert(true, '发布成功')
  30. // 3.4 重置表单并跳转到列表页
  31. form.reset()
  32. // 封面需要手动重置
  33. document.querySelector('.rounded').src = ''
  34. document.querySelector('.rounded').classList.remove('show')
  35. document.querySelector('.place').classList.remove('hide')
  36. // 富文本编辑器重置
  37. editor.setHtml('')
  38. setTimeout(() => {
  39. location.href = '../content/index.html'
  40. },1500)
  41. } catch (error) {
  42. console.dir(error)
  43. myAlert(false, error.response.data.message)
  44. }
  45. })

13.内容管理-文章列表展示

目标:获取文章列表并展示

步骤:

1.准备查询参数对象

2.获取文章列表数据

3.展示到指定的标签结构中

  1. /**
  2. * 目标1:获取文章列表并展示
  3. * 1.1 准备查询参数对象
  4. * 1.2 获取文章列表数据
  5. * 1.3 展示到指定的标签结构中
  6. */
  7. // 1.1 准备查询参数对象
  8. const queryObj = {
  9. status: '', // 文章状态(1-待审核,2-审核通过)空字符串-全部
  10. channel_id: '', // 文章频道id,空字符串-全部
  11. page: 1, // 当前页码
  12. per_page: 2 // 当前页面条数
  13. }
  14. async function setArtileList() {
  15. // 1.2 获取文章列表数据
  16. const res = await axios({
  17. url: '/v1_0/mp/articles',
  18. params: queryObj
  19. })
  20. console.log(res)
  21. // 1.3 展示到指定的标签结构中
  22. const htmlStr = res.data.results.map( item => `<tr>
  23. <td>
  24. <img src=" ${item.cover.type === 0 ? `https://img2.baidu.com/it/u=2640406343,1419332367&amp;fm=253&amp;fmt=auto&amp;app=138&amp;f=JPEG?w=708&amp;h=500` : item.cover.images[0]}" alt="">
  25. </td>
  26. <td>${item.title}</td>
  27. <td>
  28. ${item.status === 1 ? `<span class="badge text-bg-primary">待审核</span>` : `<span class="badge text-bg-success">审核通过</span>`}
  29. </td>
  30. <td>
  31. <span>${ item.pubdate }</span>
  32. </td>
  33. <td>
  34. <span> ${ item.read_count } </span>
  35. </td>
  36. <td>
  37. <span> ${ item.comment_count } </span>
  38. </td>
  39. <td>
  40. <span> ${ item.like_count }</span>
  41. </td>
  42. <td>
  43. <i class="bi bi-pencil-square edit"></i>
  44. <i class="bi bi-trash3 del"></i>
  45. </td>
  46. </tr>
  47. `).join('')
  48. // console.log(htmlStr)
  49. document.querySelector('.art-list').innerHTML = htmlStr
  50. }
  51. setArtileList()

14.内容管理-筛选功能

目标:根据筛选条件,获取匹配数据展示

步骤:

1.设置频道列表数据

2.监听筛选条件改变,保存查询信息到查询参数对象

3.点击筛选时,传递查询参数对象到服务器

4.获取匹配数据,覆盖到页面展示

  1. /**
  2. * 目标2:筛选文章列表
  3. * 2.1 设置频道列表数据
  4. * 2.2 监听筛选条件改变,保存查询信息到查询参数对象
  5. * 2.3 点击筛选时,传递查询参数对象到服务器
  6. * 2.4 获取匹配数据,覆盖到页面展示
  7. */
  8. async function setChannleList() {
  9. const res = await axios({
  10. url: '/v1_0/channels'
  11. })
  12. const htmlStr = `<option value="" selected="">请选择文章频道</option>` + res.data.channels.map(item => `<option value="${item.id}">${item.name}</option>`).join('')
  13. document.querySelector('.form-select').innerHTML = htmlStr
  14. }
  15. setChannleList()
  16. // 2.2 监听筛选条件改变,保存查询信息到查询参数对象
  17. // 筛选状态标记数字 -> change事件 -> 绑定到查询参数对象上
  18. document.querySelectorAll('.form-check-input').forEach(radio => {
  19. radio.addEventListener('change', e => {
  20. // console.log(e.target.value)
  21. queryObj.status = e.target.value
  22. })
  23. })
  24. // 筛选频道 id -> change事件 -> 绑定到查询参数对象上
  25. document.querySelector('.form-select').addEventListener('change', e =>
  26. {
  27. // console.log(e.target.value)
  28. queryObj.channel_id = e.target.value
  29. })
  30. // 2.3 点击筛选时,传递查询参数对象到服务器
  31. document.querySelector('.sel-btn').addEventListener('click', () => {
  32. // 2.4 获取匹配数据,覆盖到页面展示
  33. setArtileList()
  34. })

15.内容管理-分页功能

内容管理-分页功能

目标:完成文章列表,分页管理功能

步骤:

1.保存并设置文章总条数

2.点击下一页,做临界值判断,并切换页面参数请求最新数据

3.点击上一页,做临界值判断,并切换页面参数请求最新数据

  1. /**
  2. * 目标3:分页功能
  3. * 3.1 保存并设置文章总条数
  4. * 3.2 点击下一页,做临界值判断,并切换页码参数并请求最新数据
  5. * 3.3 点击上一页,做临界值判断,并切换页码参数并请求最新数据
  6. */
  7. // 3.2 点击下一页,做临界值判断,并切换页码参数并请求最新数据
  8. document.querySelector('.next').addEventListener('click', e => {
  9. // 当前页码小于最大页码数
  10. if (queryObj.page < Math.ceil(totalCount / queryObj.per_page)) {
  11. queryObj.page++
  12. document.querySelector('.page-now').innerHTML = `第${queryObj.page}页`
  13. setArtileList()
  14. }
  15. } )
  16. // 3.3 点击上一页,做临界值判断,并切换页码参数并请求最新数据
  17. document.querySelector('.last').addEventListener('click', e => {
  18. // 大于 1 的时候,才能翻到上一页
  19. if (queryObj.page > 1) {
  20. queryObj.page--
  21. document.querySelector('.page-now').innerHTML = `第${queryObj.page}页`
  22. }
  23. setArtileList()
  24. })

16.内容管理-删除功能

目标:完成删除文章功能

步骤:

  1. 关联文章id到删除图标
  2. 点击删除时,获取文章id
  3. 调用删除接口,传递文章id到服务器
  4. 重新获取文章列表,并覆盖展示
  1. /**
  2. * 目标4:删除功能
  3. * 4.1 关联文章 id 到删除图标
  4. * 4.2 点击删除时,获取文章 id
  5. * 4.3 调用删除接口,传递文章 id 到服务器
  6. * 4.4 重新获取文章列表,并覆盖展示
  7. * 4.5 删除最后一页的最后一条,需要自动向前翻页
  8. */
  9. // 4.2 点击删除时,获取文章 id
  10. document.querySelector('.art-list').addEventListener('click', async e => {
  11. // 判断点击的是删除元素
  12. if (e.target.classList.contains('del')) {
  13. const delId = e.target.parentNode.dataset.id
  14. console.log(delId)
  15. // 4.3 调用删除接口,传递文章 id 到服务器
  16. const res = await axios({
  17. url: `v1_0/mp/articles/${delId}` ,
  18. method: 'DELETE'
  19. })
  20. console.log(res)
  21. // 4.4 重新获取文章列表,并覆盖展示
  22. setArtileList()
  23. }
  24. })

17.内容管理-删除最后一条

目标:在删除最后一页,最后一条时有Bug

1.删除成功时,判断DOM元素只剩一条,让当前页码 page--

2.注意,当前页码为1时不能继续向前翻页

3.重新设置页码数,获取最新列表展示

  1. // 4.5 删除最后一页的最后一条,需要自动向前翻页
  2. const children = document.querySelector('.art-list').children
  3. if (children.length === 1 && queryObj.page !== 1){
  4. queryObj.page--
  5. document.querySelector('.page-now').innerHTML = `第${queryObj.page }页`
  6. }

18.内容管理-编辑文章-回显

目标:编辑文章时,回显数据到表单

步骤:

  1. 页面跳转传参(URL查询参数方式)
    1. // 点击编辑时,获取文章 id,跳转到发布文章页面传递文章 id 过去
    2. document.querySelector('.art-list').addEventListener('click', e => {
    3. if (e.target.classList.contains('edit')) {
    4. const artId = e.target.parentNode.dataset.id
    5. console.log(artId)
    6. location.href = `../publish/index.html?id=${artId}`
    7. }
    8. })
  2. 发布文章页面接收参数判断(共用同一套表单)
  3. 修改标题和按钮文字
  4. 获取文章详情数据并回显表单

对应代码:

  1. /**
  2. * 目标4:编辑-回显文章
  3. * 4.1 页面跳转传参(URL 查询参数方式)
  4. * 4.2 发布文章页面接收参数判断(共用同一套表单)
  5. * 4.3 修改标题和按钮文字
  6. * 4.4 获取文章详情数据并回显表单
  7. */
  8. ;(function(){
  9. // 4.2 发布文章页面接收参数判断(共用同一套表单)
  10. const paramsStr = location.search
  11. const params = new URLSearchParams(paramsStr)
  12. params.forEach(async (value, key) => {
  13. // 当前有要编辑的文章 id 被传入过来
  14. if (key === 'id'){
  15. // 4.3 修改标题和按钮文字
  16. document.querySelector('.title span').innerHTML = '修改文章'
  17. document.querySelector('.send').innerHTML = '修改'
  18. // 4.4 获取文章详情数据并回显表单
  19. const res = await axios({
  20. url: `/v1_0/mp/articles/${value}`
  21. })
  22. console.log(res)
  23. // 组织我仅仅需要的数据对象,为后续遍历回显到页面上做铺垫
  24. const dataObj = {
  25. channel_id: res.data.channel_id,
  26. title: res.data.title,
  27. rounded: res.data.cover.images[0], // 封面图片地址
  28. content: res.data.content,
  29. id: res.data.id
  30. }
  31. // 遍历数据对象属性,映射到页面元素上,快速赋值
  32. Object.keys(dataObj).forEach(key => {
  33. if(key === 'rounded'){
  34. // 封面设置
  35. if(dataObj[key]){
  36. // 有封面
  37. document.querySelector('.rounded').src = dataObj[key]
  38. document.querySelector('.rounded').classList.add('show')
  39. document.querySelector('.place').classList.add('hide')
  40. }
  41. } else if( key === 'content'){
  42. // 富文本内容
  43. editor.setHtml(dataObj[key])
  44. } else {
  45. // 用数据对象属性名,作为标签 name 属性选择器值来找到匹配的标签
  46. document.querySelector(`[name=${key}]`).value = dataObj[key]
  47. }
  48. })
  49. }
  50. })
  51. })();

19.内容管理-编辑文章-保存

目标:确认修改,保存文章到服务器

步骤:

1.判断按钮文字,区分业务(因为共用一套表单)

2.调用编辑文章接口,保存信息到服务器

3.基于Alert反馈结果消息给用户

  1. /**
  2. * 目标5:编辑-保存文章
  3. * 5.1 判断按钮文字,区分业务(因为共用一套表单)
  4. * 5.2 调用编辑文章接口,保存信息到服务器
  5. * 5.3 基于 Alert 反馈结果消息给用户
  6. */
  7. document.querySelector('.send').addEventListener('click', async e => {
  8. // 5.1 判断按钮文字,区分业务(因为共用一套表单)
  9. if (e.target.innerHTML !== '修改') return
  10. // 修改文章逻辑
  11. const form = document.querySelector('.art-form')
  12. const data = serialize(form, { hash: true, empty: true})
  13. // console.log(data)
  14. // 5.2 调用编辑文章接口,保存信息到服务器
  15. try {
  16. const res = await axios({
  17. url: `/v1_0/mp/articles/${data.id}`,
  18. method: 'PUT',
  19. data: {
  20. ...data,
  21. cover:{
  22. type: document.querySelector('.rounded').src ? 1 : 0,
  23. images: [document.querySelector('.rounded').src]
  24. }
  25. }
  26. })
  27. console.log(res)
  28. myAlert(true,'修改文章成功')
  29. } catch (error) {
  30. myAlert(false,error.response.data.message)
  31. }
  32. })

20.退出登录

目标:完成退出登录效果

步骤:

1.绑定点击事件

2.清空本地缓存,跳转到登录页面

  1. /**
  2. * 目标3:退出登录
  3. * 3.1 绑定点击事件
  4. * 3.2 清空本地缓存,跳转到登录页面
  5. */
  6. document.querySelector('.quit').addEventListener('click', e => {
  7. // 3.2 清空本地缓存,跳转到登录页面
  8. localStorage.clear()
  9. location.href = '../login/index.html'
  10. })

项目素材代码点击这里:【免费】黑马头条数据管理平台的素材、代码资源-CSDN文库

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/509881
推荐阅读
相关标签
  

闽ICP备14008679号