目录

项目介绍

功能

项目准备

技术

验证码

验证码登录

验证码登录-流程

关于token

token的介绍

token的使用

个人信息设置

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

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

优化-axios响应结果

发布文章

发布文章-富文本编辑器

发布文章-频道列表

发布文章-封面设置

发布文章-收集并保存

内容管理

内容管理-文章列表展示

内容管理-筛选功能

内容管理-分页功能

内容管理-删除功能

内容管理-删除最后一条

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

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

退出登录

项目介绍

黑马头条-数据管理平台:对IT资讯移动网站的数据,进行数据管理

数据管理平台-演示:配套代码在本地运行

移动网站-演示:http://geek.itheima.net/


 

功能

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

总结:

1. 黑马头条-数据管理平台,是什么样网站,要完成哪些功能?

  • 数据管理网站, 登录后对数据进行增删改查

2. 数据管理平台,未登录能否管理数据?

  • 不能,数据是公司内部的,需账号登录后管理

项目准备

技术

  • 基于Bootstrap搭建网站标签和样式
  • 集成wangEditor插件实现富文本编辑器
  • 使用原生JS完成增删改查等业务
  • 基于axios与黑马头条线上接口交互
  • 使用axios拦截器进行权限判断
     

项目准备:准备配套的素材代码

包含:html,CSS,js,静态图片,第三方插件等等

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

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

总结:

1. 为什么要按照一定的结构,管理代码文件?

  • 方便以后的查找扩展

验证码

验证码登录

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

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

步骤:

1. 在utils/request.js配置axios请求基地址

  • 作用:提取公共前缀地址,配置后axios请求时都会baseURL + url

2. 收集手机号和验证码数据

3. 基于axios调用验证码登录接口

4. 使用Bootstrap的Alert警告框反馈结果给用户


验证码登录-流程

手机号+验证码,登录流程:


 

关于token

token的介绍

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

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


 

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

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

token的使用

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

步骤:

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

总结:

1. token的作用?

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

2. token的注意:

  • 前端只能判断token的有无
  • 后端通过解密可以提取token字符串的原始信息,判断有效性
     
/*** 目标1:验证码登录* 1.1 在 utils/request.js 配置 axios 请求基地址* 1.2 收集手机号和验证码数据* 1.3 基于 axios 调用验证码登录接口* 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户*/// 1.2 收集手机号和验证码数据
document.querySelector('.btn').addEventListener('click', () => {const form = document.querySelector('.login-form')const data = serialize(form, { hash: true, empty: true })console.log(data)// 1.3 基于 axios 调用验证码登录接口axios({url: '/v1_0/authorizations',method: 'POST',data}).then(result => {// 1.4 使用 Bootstrap 的 Alert 警告框反馈结果给用户myAlert(true, '登录成功')console.log(result)// 登录成功后,保存 token 令牌字符串到本地,并跳转到内容列表页面localStorage.setItem('token', result.data.token)setTimeout(() => {// 延迟跳转,让 alert 警告框停留一会儿location.href = '../content/index.html'}, 1500)}).catch(error => {myAlert(false, error.response.data.message)console.dir(error.response.data.message)})
})

个人信息设置

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

需求:设置用户昵称

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


 

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

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


 

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

总结:

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

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

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

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

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

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

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


 

// axios 公共配置
// 基地址
axios.defaults.baseURL = 'http://geek.itheima.net'// 添加请求拦截器
axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么// 统一携带 token 令牌字符串在请求头上const token = localStorage.getItem('token')token && (config.headers.Authorization = `Bearer ${token}`)return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
axios.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么return response;
}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么,例如:统一对 401 身份验证失败情况做出处理console.dir(error)if (error?.response?.status === 401) {alert('身份验证失败,请重新登录')localStorage.clear()location.href = '../login/index.html'}return Promise.reject(error);
});

总结:

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

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

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

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

优化-axios响应结果

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


 

// axios 公共配置
// 基地址
axios.defaults.baseURL = 'http://geek.itheima.net'// 添加请求拦截器
axios.interceptors.request.use(function (config) {// 在发送请求之前做些什么// 统一携带 token 令牌字符串在请求头上const token = localStorage.getItem('token')token && (config.headers.Authorization = `Bearer ${token}`)return config;
}, function (error) {// 对请求错误做些什么return Promise.reject(error);
});// 添加响应拦截器
axios.interceptors.response.use(function (response) {// 2xx 范围内的状态码都会触发该函数。// 对响应数据做点什么,例如:直接返回服务器的响应结果对象const result = response.datareturn result;
}, function (error) {// 超出 2xx 范围的状态码都会触发该函数。// 对响应错误做点什么,例如:统一对 401 身份验证失败情况做出处理console.dir(error)if (error?.response?.status === 401) {alert('身份验证失败,请重新登录')localStorage.clear()location.href = '../login/index.html'}return Promise.reject(error);
});

发布文章

发布文章-富文本编辑器

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

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


 

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

使用:wangEditor 插件

步骤:参考文档

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

// 富文本编辑器
// 创建编辑器函数,创建工具栏函数
const { createEditor, createToolbar } = window.wangEditor// 编辑器配置对象
const editorConfig = {// 占位提示文字placeholder: '发布文章内容...',// 编辑器变化时回调函数onChange(editor) {// 获取富文本内容const html = editor.getHtml()// 也可以同步到 <textarea>// 为了后续快速收集整个表单内容做铺垫document.querySelector('.publish-content').value = html}
}// 创建编辑器
const editor = createEditor({// 创建位置selector: '#editor-container',// 默认内容html: '<p><br></p>',// 配置项config: editorConfig,// 配置集成模式(default 全部)(simple 简洁)mode: 'default', // or 'simple'
})// 工具栏配置对象
const toolbarConfig = {}// 创建工具栏
const toolbar = createToolbar({// 为指定编辑器创建工具栏editor,// 工具栏创建的位置selector: '#toolbar-container',// 工具栏配置对象config: toolbarConfig,// 配置集成模式mode: 'default', // or 'simple'
})

发布文章-频道列表

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

步骤:

  1. 获取频道列表数据
  2. 展示到下拉菜单中
     

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

发布文章-封面设置

目标:文章封面的设置

步骤:

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

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

/*** 目标2:文章封面设置*  2.1 准备标签结构和样式*  2.2 选择文件并保存在 FormData*  2.3 单独上传图片并得到图片 URL 网址*  2.4 回显并切换 img 标签展示(隐藏 + 号上传标签)*/
// 2.2 选择文件并保存在 FormData
document.querySelector('.img-file').addEventListener('change', async e => {const file = e.target.files[0]const fd = new FormData()fd.append('image', file)// 2.3 单独上传图片并得到图片 URL 网址const res = await axios({url: '/v1_0/upload',method: 'POST',data: fd})// 2.4 回显并切换 img 标签展示(隐藏 + 号上传标签)const imgUrl = res.data.urldocument.querySelector('.rounded').src = imgUrldocument.querySelector('.rounded').classList.add('show')document.querySelector('.place').classList.add('hide')
})
// 优化:点击 img 可以重新切换封面
// 思路:img 点击 => 用 JS 方式触发文件选择元素 click 事件方法
document.querySelector('.rounded').addEventListener('click', () => {document.querySelector('.img-file').click()
})

发布文章-收集并保存

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

步骤:

  1. 基于form-serialize插件收集表单数据对象
  2. 基于axios提交到服务器保存
  3. 调用Alert警告框反馈结果给用户
  4. 重置表单并跳转到列表页
     

/*** 目标3:发布文章保存*  3.1 基于 form-serialize 插件收集表单数据对象*  3.2 基于 axios 提交到服务器保存*  3.3 调用 Alert 警告框反馈结果给用户*  3.4 重置表单并跳转到列表页*/
// 3.1 基于 form-serialize 插件收集表单数据对象
document.querySelector('.send').addEventListener('click', async e => {if (e.target.innerHTML !== '发布') returnconst form = document.querySelector('.art-form')const data = serialize(form, { hash: true, empty: true })// 发布文章的时候,不需要 id 属性,所以可以删除掉(id 为了后续做编辑使用)delete data.idconsole.log(data)// 自己收集封面图片地址并保存到 data 对象中data.cover = {type: 1, // 封面类型images: [document.querySelector('.rounded').src] // 封面图片 URL 网址}// 3.2 基于 axios 提交到服务器保存try {const res = await axios({url: '/v1_0/mp/articles',method: 'POST',data: data})// 3.3 调用 Alert 警告框反馈结果给用户myAlert(true, '发布成功')// 3.4 重置表单并跳转到列表页form.reset()// 封面需要手动重置document.querySelector('.rounded').src = ''document.querySelector('.rounded').classList.remove('show')document.querySelector('.place').classList.remove('hide')// 富文本编辑器重置editor.setHtml('')setTimeout(() => {location.href = '../content/index.html'}, 1500)} catch (error) {myAlert(false, error.response.data.message)}
})

内容管理

内容管理-文章列表展示

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

步骤:

  1. 准备查询参数对象
  2. 获取文章列表数据
  3. 展示到指定的标签结构中
     

/*** 目标1:获取文章列表并展示*  1.1 准备查询参数对象*  1.2 获取文章列表数据*  1.3 展示到指定的标签结构中*/
// 1.1 准备查询参数对象
const queryObj = {status: '', // 文章状态(1-待审核,2-审核通过)空字符串-全部channel_id: '', // 文章频道 id,空字符串-全部page: 1, // 当前页码per_page: 2 // 当前页面条数
}
let totalCount = 0 // 保存文章总条数// 获取并设置文章列表
async function setArtileList() {// 1.2 获取文章列表数据const res = await axios({url: '/v1_0/mp/articles',params: queryObj})// 1.3 展示到指定的标签结构中const htmlStr = res.data.results.map(item => `<tr><td><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=""></td><td>${item.title}</td><td>${item.status === 1 ? `<span class="badge text-bg-primary">待审核</span>` : `<span class="badge text-bg-success">审核通过</span>`}</td><td><span>${item.pubdate}</span></td><td><span>${item.read_count}</span></td><td><span>${item.comment_count}</span></td><td><span>${item.like_count}</span></td><td data-id="${item.id}"><i class="bi bi-pencil-square edit"></i><i class="bi bi-trash3 del"></i></td>
</tr>`).join('')document.querySelector('.art-list').innerHTML = htmlStr// 3.1 保存并设置文章总条数totalCount = res.data.total_countdocument.querySelector('.total-count').innerHTML = `共 ${totalCount} 条`
}
setArtileList()

内容管理-筛选功能

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

步骤:

  1. 设置频道列表数据
  2. 监听筛选条件改变,保存查询信息到查询参数对象
  3. 点击筛选时,传递查询参数对象到服务器
  4. 获取匹配数据,覆盖到页面展示
     

/*** 目标2:筛选文章列表*  2.1 设置频道列表数据*  2.2 监听筛选条件改变,保存查询信息到查询参数对象*  2.3 点击筛选时,传递查询参数对象到服务器*  2.4 获取匹配数据,覆盖到页面展示*/
// 2.1 设置频道列表数据
async function setChannleList() {const res = await axios({url: '/v1_0/channels'})const htmlStr = `<option value="" selected="">请选择文章频道</option>` + res.data.channels.map(item => `<option value="${item.id}">${item.name}</option>`).join('')document.querySelector('.form-select').innerHTML = htmlStr
}
setChannleList()
// 2.2 监听筛选条件改变,保存查询信息到查询参数对象
// 筛选状态标记数字->change事件->绑定到查询参数对象上
document.querySelectorAll('.form-check-input').forEach(radio => {radio.addEventListener('change', e => {queryObj.status = e.target.value})
})
// 筛选频道 id -> change事件 -> 绑定到查询参数对象上
document.querySelector('.form-select').addEventListener('change', e => {queryObj.channel_id = e.target.value
})
// 2.3 点击筛选时,传递查询参数对象到服务器
document.querySelector('.sel-btn').addEventListener('click', () => {// 2.4 获取匹配数据,覆盖到页面展示setArtileList()
})

内容管理-分页功能

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

步骤:

  1. 保存并设置文章总条数
  2. 点击下一页,做临界值判断,并切换页码参数请求最新数据
  3. 点击上一页,做临界值判断,并切换页码参数请求最新数据
     

/*** 目标3:分页功能*  3.1 保存并设置文章总条数*  3.2 点击下一页,做临界值判断,并切换页码参数并请求最新数据*  3.3 点击上一页,做临界值判断,并切换页码参数并请求最新数据*/
// 3.2 点击下一页,做临界值判断,并切换页码参数并请求最新数据
document.querySelector('.next').addEventListener('click', e => {// 当前页码小于最大页码数if (queryObj.page < Math.ceil(totalCount / queryObj.per_page)) {queryObj.page++document.querySelector('.page-now').innerHTML = `第 ${queryObj.page} 页`setArtileList()}
})
// 3.3 点击上一页,做临界值判断,并切换页码参数并请求最新数据
document.querySelector('.last').addEventListener('click', e => {// 大于 1 的时候,才能翻到上一页if (queryObj.page > 1) {queryObj.page--document.querySelector('.page-now').innerHTML = `第 ${queryObj.page} 页`setArtileList()}
})

内容管理-删除功能

目标:完成删除文章功能.

步骤:

  1. 关联文章id到删除图标
  2. 点击删除时,获取文章id
  3. 调用删除接口,传递文章id到服务器
  4. 重新获取文章列表,并覆盖展示
     

内容管理-删除最后一条

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

解决:

  1. 删除成功时,判断DOM元素只剩一条,让当前页码page--
  2. 注意,当前页码为1时不能继续向前翻页
  3. 重新设置页码数,获取最新列表展示
     

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

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

步骤:

  1. 页面跳转传参(URL 查询参数方式)
  2. 发布文章页面接收参数判断(共用同一套表单)
  3. 修改标题和按钮文字
  4. 获取文章详情数据并回显表单
     

/*** 目标4:编辑-回显文章*  4.1 页面跳转传参(URL 查询参数方式)*  4.2 发布文章页面接收参数判断(共用同一套表单)*  4.3 修改标题和按钮文字*  4.4 获取文章详情数据并回显表单*/; (function () {// 4.2 发布文章页面接收参数判断(共用同一套表单)const paramsStr = location.searchconst params = new URLSearchParams(paramsStr)params.forEach(async (value, key) => {// 当前有要编辑的文章 id 被传入过来if (key === 'id') {// 4.3 修改标题和按钮文字document.querySelector('.title span').innerHTML = '修改文章'document.querySelector('.send').innerHTML = '修改'// 4.4 获取文章详情数据并回显表单const res = await axios({url: `/v1_0/mp/articles/${value}`})console.log(res)// 组织我仅仅需要的数据对象,为后续遍历回显到页面上做铺垫const dataObj = {channel_id: res.data.channel_id,title: res.data.title,rounded: res.data.cover.images[0], // 封面图片地址content: res.data.content,id: res.data.id}// 遍历数据对象属性,映射到页面元素上,快速赋值Object.keys(dataObj).forEach(key => {if (key === 'rounded') {// 封面设置if (dataObj[key]) {// 有封面document.querySelector('.rounded').src = dataObj[key]document.querySelector('.rounded').classList.add('show')document.querySelector('.place').classList.add('hide')}} else if (key === 'content') {// 富文本内容editor.setHtml(dataObj[key])} else {// 用数据对象属性名,作为标签 name 属性选择器值来找到匹配的标签document.querySelector(`[name=${key}]`).value = dataObj[key]}})}})})();

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

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

步骤:

  1. 判断按钮文字,区分业务(因为共用一套表单)
  2. 调用编辑文章接口,保存信息到服务器
  3. 基于Alert反馈结果消息给用户
     

/*** 目标5:编辑-保存文章*  5.1 判断按钮文字,区分业务(因为共用一套表单)*  5.2 调用编辑文章接口,保存信息到服务器*  5.3 基于 Alert 反馈结果消息给用户*/
document.querySelector('.send').addEventListener('click', async e => {// 5.1 判断按钮文字,区分业务(因为共用一套表单)if (e.target.innerHTML !== '修改') return// 修改文章逻辑const form = document.querySelector('.art-form')const data = serialize(form, { hash: true, empty: true })// 5.2 调用编辑文章接口,保存信息到服务器try {const res = await axios({url: `/v1_0/mp/articles/${data.id}`,method: 'PUT',data: {...data,cover: {type: document.querySelector('.rounded').src ? 1 : 0,images: [document.querySelector('.rounded').src]}}})console.log(res)myAlert(true, '修改文章成功')} catch (error) {myAlert(false, error.response.data.message)}
})

退出登录

目标:完成退出登录效果

步骤:

  1. 绑定点击事件
  2. 清空本地缓存,跳转到登录页面
     

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/86100.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/86100.shtml
英文地址,请注明出处:http://en.pswp.cn/web/86100.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Pytorch3D 中涉及的知识点汇总

PyTorch3D 是 Facebook&#xff08;现 Meta&#xff09;AI 研究院&#xff08;FAIR&#xff09;推出的一个基于 PyTorch 的三维计算库&#xff0c;主要用于 3D 计算机视觉与图形学任务&#xff0c;如 3D 重建、渲染、点云处理、网格操作等。 下面是对 PyTorch3D 中重要涉及知识…

XML在线格式化工具

XML格式化 免费在线XML格式化与压缩工具&#xff0c;一键美化、校验、压缩和优化您的XML代码。支持自定义缩进、节点折叠&#xff0c;提升可读性&#xff0c;减小文件体积&#xff0c;加速数据传输。 https://toolshu.com/xml 本工具是一款专为处理XML&#xff08;可扩展标记…

【软件系统架构】系列四:嵌入式技术

目录 一、嵌入式系统组成 (1)嵌入式处理器 (2)支撑硬件 (3)嵌入式操作系统 (4)支撑软件 (5)应用软件 二、嵌入式系统特性 三、嵌入式系统分类与分层结构 1.分类 2.嵌入式软件的五层架构深入解析 (1)硬件层(Hardware Layer) (2)抽象层(Hardware Ab…

监管报送面试回答思路和示例

在银行监管报送岗位的面试中&#xff0c;回答问题时需要展现出你的专业知识、实际操作经验、问题解决能力以及对监管合规的重视。以下是对各类问题的回答思路和示例&#xff1a; 一、专业知识类问题 1. 请简述银行监管报送的主要类型和报送频率 回答思路&#xff1a;分类介绍…

音视频SDK架构演进的实践与思考

“不是每一行代码都值得骄傲&#xff0c;但每一次迭代&#xff0c;都是一次更接近极致的尝试。” 从最初的数千行代码、到如今跨平台、全功能、稳定可靠的直播技术基座&#xff0c;大牛直播SDK走过了整整十年。十年&#xff0c;既是时间的刻度&#xff0c;更是技术沉淀与产品信…

vue.config.js配置学习

1.部署应用包时的基本 URL (baseUrl或publicPath) baseUrl在vue-cli 3.3 时弃用了&#xff0c;自此之后使用publicPath 默认&#xff1a;/ module.exports {// baseUrl:"/",publicPath: ./, ) 2.打包时输出的文件位置&#xff1a;outputDir 默认: dist module.…

大模型——Prompt Design

Prompt Design 为什么未来最重要的写作,不是写给人看的,而是写给AI理解的? 01|一切从一次“客服神操作”开始 前几天前,我在看一场 YC Demo Day 分享的时候,听到一个很炸裂的细节: 有个叫 Parahelp 的 AI 客服创业项目,靠一段几百行的“提示词”,打败了市面上大多数…

web布局20

在当下&#xff0c;可用于 Web 布局的 CSS 特性有很多&#xff0c;而且这个集合越来越强大。自从 Flexbox 的兼容性越来越完善&#xff0c;它替代了浮动布局&#xff0c;成为主流的布局技术。只不过&#xff0c;近几年来&#xff0c;CSS Grid 快速得到主流浏览器的支持&#xf…

数据集-目标检测系列- 餐具叉子 数据集 fork >> DataBall

数据集-目标检测系列- 餐具叉子 数据集 fork &#xff1e;&#xff1e; DataBall 贵在坚持&#xff01; * 相关项目 1&#xff09;数据集可视化项目&#xff1a;gitcode: https://gitcode.com/DataBall/DataBall-detections-100s/overview 2&#xff09;数据集训练、推理相…

力扣-45.跳跃游戏 ll

题目描述 给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。 每个元素 nums[i] 表示从索引 i 向后跳转的最大长度。换句话说&#xff0c;如果你在 nums[i] 处&#xff0c;你可以跳转到任意 nums[i j] 处: 0 < j < nums[i] i j < n 返回到达 nums[n…

借助STL工具解题的各个技巧

目录 前言 STL容器一览 set和map如何降序构建 set和map如何插入自定义对象 multiset和multimap如何降序构建 multiset和multimap如何插入自定义对象 multi_系列如何equal_range multiset multimap unorder_multiset unorder_multimap STL容器迭代器一览 迭代器性能一览…

【Linux 设备树DTS】

Linux 设备树DTS 一、设备树概述&#xff1a;为什么它是 Linux 驱动开发的关键&#xff1f;二、设备树语法详解&#xff1a;从基础到高级2.1 基本结构&#xff1a;节点与属性2.2 数据类型与表示方式2.3 引用与别名2.4 address-cells和size-cells属性详解2.5 包含与覆盖2.6 未定…

【技巧】使用frpc安全地内网穿透ssh访问内网机器

【技巧】使用frpc安全地内网穿透ssh访问内网机器 0. 为什么需要部署 office的机器可以单向访问互联网&#xff0c;互联网无法直接访问到这台机器。有时候需要从家里通过ssh远程访问和配置。 在云服务器上部署frpc把转发ssh给需要访问的机器。 1. 互联网云服务器&#xff08;…

【Faster-Whisper】离线识别本地视频并生成字幕

【Faster-Whisper】离线识别本地视频并生成字幕 1 前言2 工具说明2.1 ffmpeg 媒体转换器2.1.1 理论简介文档 2.1.2 安装win安装python安装 2.1.3 查看查看音视频文件格式、编码 2.1.4 视频处理视频格式转换设置 视频码率裁剪视频 2.1.5 音频处理视频提取音频音频格式转换gpu加速…

开源CMS vs 闭源CMS:二次开发究竟有何不同?

在网站建设项目中&#xff0c;内容管理系统&#xff08;CMS&#xff09; 是核心基础设施。而“二次开发”则是让CMS真正适配业务需求的关键环节&#xff0c;譬如调整页面样式&#xff0c;或者新增会员体系等等。但很多人没意识到&#xff1a;选择开源CMS还是闭源CMS&#xff0c…

npm 更新包名,本地导入

package.json 更新包根目录名字&#xff0c;同时改 name 和 dependencies相关的依赖也需本地导入&#xff0c;否则无法生效 之后将改包放在你所需的项目位置&#xff0c;通过以下命令导入node_modules生效 pnpm install file:../table-ui/m-table -w防止包数据更新或丢弃&…

若依框架二次开发——若依前后端分离版集成 UReport2 报表工具

文章目录 一、UReport2 简介二、解决方案1、后端配置1.1 引入 UReport2 依赖1.2 启动类配置1.3配置文件1.4 修改安全配置2、前端配置2.1 配置 Vue.js 代理2.2创建设计器页面2.3 新增菜单运行结果一、UReport2 简介 UReport2 是一款开源的 Java 报表工具,广泛应用于各类企业管…

Ntfs!_LFCB结构如何构建出来的--从Ntfs!NtfsMountVolume到Ntfs!LfsAllocateLfcb

Ntfs!LfsRestartLogFile函数分析之调用Ntfs!LfsAllocateLfcb函数初始化Lfcb->LbcbWorkque 第一部分&#xff1a; F:\srv03rtm>grep "NtfsStartLogFile" -rn F:\srv03rtm\base\fs\ntfs |grep -v "inary" F:\srv03rtm\base\fs\ntfs/fsctrl.c:1890: …

Domain层到底是什么

层级主要职责是否依赖 iOS / UIKit&#xff1f;Presentation (UI)视图、控制器、ViewModel&#xff0c;将用户操作转成「意图」&#xff0c;把结果渲染到屏幕是Domain业务规则 与 用例 (Use Case)&#xff0c;维护系统在概念上的真实世界模型否&#xff08;纯 Swift&#xff0c…

Rust 服务端项目分层结构

DDD src/ ├── main.rs # 程序入口&#xff0c;负责启动和依赖注入 ├── lib.rs # 公共库入口&#xff0c;便于单元测试和复用 ├── config.rs # 配置管理&#xff08;如数据库、端口、环境变量等&#xff09; ├── entities/ …