当前位置: 首页 > news >正文

文书写作网站网上备案

文书写作网站,网上备案,简述网站开发平台及常用工具,企业计划书模板范文提示#xff1a;文章写完后#xff0c;目录可以自动生成#xff0c;如何生成可参考右边的帮助文档 文章目录前言1. 管理员登录前端1.1 测试1.2 同源策略1.3 修改前端端口号1.4 跨域问题1.5 接收响应数据1.6 js-cookie1.7 错误消息提示1.8 优化1.9 响应拦截器1.10 用法2. 后台… 提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档 文章目录前言1. 管理员登录前端1.1 测试1.2 同源策略1.3 修改前端端口号1.4 跨域问题1.5 接收响应数据1.6 js-cookie1.7 错误消息提示1.8 优化1.9 响应拦截器1.10 用法2. 后台管理-布局2.1 点击跳转不同页面3. 获取当前用户信息3.1 数据库修改3.2 设计3.3 开发后端3.4 开发前端4. 退出登录4.1 业务分析4.2 后端开发4.3 前端开发5. 前端路由优化5.1 重定向5.2 全局前置守卫5.3 token过期处理总结前言 1. 管理员登录前端 1.1 测试 测试一下发现报了这个错 这个主要是因为我们配置的前置url没有含有http协议所以浏览器就会自动加上静态资源的url const service axios.create({baseURL: http://127.0.0.1:19090/system,timeout: 1000, })这样就Ok了 但是又出了一个新的问题 这个就是跨域问题 1.2 同源策略 1.3 修改前端端口号 在vite.config.js里面添加 server: {port: 5555,}这样就可以了 1.4 跨域问题 我们可以用一个代理服务器来处理 浏览器前端先请求同源的代理服务器然后代理服务器把请求转发到后端 因为浏览器有同源策略的约束 但是代理服务器是没有同源策略的约束的 server: {proxy: {/dev-api: {target: http://127.0.0.1:19090/system,rewrite: (p) p.replace(/^\/dev-api/, ),},},},还是在vite.config.js里面这样设置就可以了 这个就是对代理规则的配置 然后修改request.js里面的前置url const service axios.create({baseURL: /dev-api,timeout: 1000, })如果没有加协议的话浏览器会把前端的url拼接到baseURL上 所以请求地址为 http://localhost:5173/dev-api/sysUser/login 这个不会发生跨域问题 这个是同源的 会报404找不到吗 不会 因为配置了代理规则 代理规则就是前缀包含/dev-api的时候就会把请求转发到http://127.0.0.1:19090/system rewrite: (p) p.replace(/^\/dev-api/, ),这个就是把/dev-api变为空的字符串 所以http://localhost:5173/dev-api/sysUser/login就会变为 http://127.0.0.1:19090/system/sysUser/login 先把http://localhost:5173替换为http://127.0.0.1:19090/system然后去掉/dev-api就OK了 补充一下axios可以自动转换JSON数据所以那样写没有问题 测试一下也是没有问题的 1.5 接收响应数据 但是axios这个调用接口的过程是一个异步的过程 往往不那么好拿到返回结果 所以要用await去获取异步操作结果 但是对应的调用他的函数也要为async async function loginFun() {const res await loginService(userAccount.value, password.value);if(res.data.code 1000){console.log(登录成功 , res.data);}else{console.log(res.data.msg)} }async 表示这个函数要使用awaitawait表示调用这个方法接口的时候用异步的方式 console.log(“登录成功” , res.data);这里如果是 console.log(“登录成功” res.data); 那么res.data打印出的内容是object 如果要打印出类的详细数据的话还是得用逗号隔开 登录成功要跳转页面 我们用router的push方法就可以了 {path: /oj/system,name: system,component: () import(../views/System.vue)}记得还要配置路由 import router from /router router.push(/oj/system)这样就可以跳转了 1.6 js-cookie 登录成功以后要存储token 怎么存储呢 存储方式有很多种 比如cookie和local-storige 我们这里使用cookie存储 js-cookie就是来操作cookie的 npm install js-cookie在utils下创建cookie.js import Cookies from js-cookie; const TokenKey Admin-Oj-b-Token; export function getToken() {return Cookies.get(TokenKey); } export function setToken(token) {return Cookies.set(TokenKey, token); } export function removeToken() {return Cookies.remove(TokenKey); }这样就可以了 import { setToken } from /utils/cookie setToken(res.data.data)在appication.cookies那里就可以看到我们设置的cookie了 1.7 错误消息提示 import { ElMessage } from element-plus ElMessage.error(res.data.msg)1.8 优化 el-input v-modelpassword typepassword show-password placeholder请输入密码 /给按钮加上 type“password” 就可以把密码隐藏起来了 show-password就是显示是不是显示小眼睛 1.9 响应拦截器 我们在request.js里面设置 service.interceptors.response.use((res) {// 未设置状态码则默认成功状态const code res.data.code;const msg res.data.msg;if (code ! 1000) {ElMessage.error(msg);return Promise.reject(new Error(msg));} else {return Promise.resolve(res.data);}},(error) {return Promise.reject(error);} );为什么要用响应拦截器呢 因为我们可以用响应拦截器对响应进行拦截可以直接返回后端返回的result数据 现在我们就在登录成功和失败的情况下分别测试一下 const res await loginService(userAccount.value, password.value);console.log(登录成功 , res.data);我们可以看出登录成功返回的数据就是后端返回的result没有进行分装了 这个就是Promise.resolve(res.data)的作用 而Promise.reject(new Error(msg))就是相当于返回一个异常了 直接报错了 (error) {return Promise.reject(error);}这里是属于错误返回其他的都是正常返回 我们控制台肯定不能这样打印的因为这样打印就是相当于出错了 所以我们还要捕获异常 就和后端捕获异常是一样的写法 async function loginFun() {try{const res await loginService(userAccount.value, password.value);setToken(res.data.data)router.push(/oj/system)console.log(登录成功 , res.data);}catch(err){console.log(登录失败 , err);} }这样就可以了 1.10 用法 import { setToken } from /utils/cookie这里的是什么意思呢 resolve: {alias: {: fileURLToPath(new URL(./src, import.meta.url))},},其实在vite.config.js里面就配置过了 就是./src所以很方便使用 相应的router这里的两点我们也可以改为 2. 后台管理-布局 创建一个布局的文件 Layout.vue templateel-container classlayout-containerel-header classel-headerel-dropdownspan classel-dropdown__boxdivstrong当前用户/strong超级管理员/divel-iconArrowDownBold //el-icon!-- el-iconLock //el-icon --/spantemplate #dropdownel-dropdown-menuel-dropdown-item clicklogout :iconSwitchButton退出登录/el-dropdown-item/el-dropdown-menu/template/el-dropdown/el-headerel-main classlayout-bottom-boxdiv classleftel-aside width200px classel-asideel-menu classel-menu routerel-menu-item index/oj/layout/cuserel-iconManagement //el-iconspan用户管理/span/el-menu-itemel-menu-item index/oj/layout/questionel-iconManagement //el-iconspan题目管理/span/el-menu-itemel-menu-item index/oj/layout/examel-iconManagement //el-iconspan竞赛管理/span/el-menu-item/el-menu/el-aside/divdiv classrightRouterView //div/el-main/el-container /templatescript setup import {Management,ArrowDownBold,Lock,SwitchButton } from element-plus/icons-vue /scriptstyle langscss scoped .layout-container {height: 100vh;background: #f7f7f7;.layout-bottom-box {display: flex;justify-content: space-between;height: calc(100vh - 100px);overflow: hidden;.left {margin-right: 20px;background: #fff;display: flex;:deep(.el-menu) {flex: 1;.el-menu-item.is-active {color: #32c5ff;}.el-menu-item:hover {background: #fff;color: #32c5ff;}}}.right {flex: 1;overflow-y: auto;background: #fff;padding: 20px;}}.el-aside {background-color: #fff;__logo {height: 120px;// background: url(/assets/logo.png) no-repeat center / 120px auto;}.el-menu {border-right: none;}}.el-header {background-color: #fff;display: flex;align-items: center;justify-content: flex-end;height: 40px;.el-dropdown__box {display: flex;align-items: center;.el-icon {color: #4c4141;margin-left: 20px;}:active,:focus {outline: none;}}}.el-footer {display: flex;align-items: center;justify-content: center;font-size: 14px;color: #666;} } /style然后配置路由 {path: /oj/layout,name: layout,component: () import(/views/Layout.vue)}这样就可以了 现在分析一下 分成了三个部分 我们用的是是这个container布局容器 el-container classlayout-containerel-header classel-header/el-headerel-main classlayout-bottom-boxdiv classleftel-aside width200px classel-aside/el-aside/divdiv classrightRouterView //div/el-main/el-container 这个就是布局 整体结构 dropdown是一个下拉菜单 ArrowDownBold是图标 import {Management,ArrowDownBold,Lock,SwitchButton } from element-plus/icons-vue但是使用图标要在js里面import SwitchButton也是图标 el-aside我们用的是el-menu来写的 2.1 点击跳转不同页面 div classrightRouterView //div这里就是根据不同url渲染不同页面就是主要的页面内容 先创建对应的vue文件 然后是配置router 怎么实现点击切换不url呢 官网的menu组件有一个router属性 这样就可以实现点击切换url了 首先先加上属性router 因为默认为false不启用加上就启动了 el-menu classel-menu router在el-menu这里加上router属性表示启动router 然后还不行因为点击要跳转到哪里呢 这下就要设置index了 所以index设置为路径就可以了 el-menu-item index/oj/question这样设置就可以了 但是点击跳转直接跳转到新的页面了而不是在那个主页面展示 为什么会这样呢 因为我们配置的路径是/oj/question与/oj/layout是同一级的 这个路由发生改变之后 会触发routerview但是这个触发的routerview是app.vue那里的 {path: /oj/layout,name: layout,component: () import(/views/Layout.vue)},{path: /oj/cuser,name: cuser,component: () import(/views/Cuser.vue)},这里的配置路径就是同一级的所以改变路径的时候就是触发的同一个routerview因为这两个的路径的配置是类似的所以layout能触发那个routerview为什么cuser不行呢 所以cuser也是用的app.vue的routerview 所以我们需要把cuser的路由配置到layout里面去 因为cuser是在layout内部进行的页面渲染 因为渲染的顺序就是先渲染layout然后是点击在layout里面渲染cuser 所以路径的配置就必须在layout里面进行配置 所以cuser的路由就是layout下的路由 路由提供了一个child的属性就可以配置了这个是数组 {path: /oj/layout,name: layout,component: () import(/views/Layout.vue),children: [{path: /cuser,name: cuser,component: () import(/views/Cuser.vue)},{path: /exam,name: exam,component: () import(/views/Exam.vue)},{path: /question,name: question,component: () import(/views/Question.vue)},]},这样就可以了 其中cuser的路径就是/oj/layout/cuser 会自动加上父组件的路径的 router.push(/oj/layout)然后登录成功的跳转也要改了 el-menu-item index/oj/layout/cuser然后这里也要改 但是还是不行 div classrightRouterView //div这里的routerview是二级目录 这里跳转显示的是二级路由而app.vue里面显示跳转的是一级路由 这里的情况是在一级小的页面里面有二级页面所以对应也要在app.vuede的routerview里面在嵌套一个routerview 因为页面有嵌套关系 所以routerview也要有嵌套关系路由配置也要有嵌套关系 {path: /oj/layout,name: layout,component: () import(/views/Layout.vue),children: [{path: cuser,name: cuser,component: () import(/views/Cuser.vue)},{path: exam,name: exam,component: () import(/views/Exam.vue)},{path: question,name: question,component: () import(/views/Question.vue)},]},注意这里的cuser的路径前面就不要加上/了因为这样可能表示是以/cser开头的是绝对路径如果是二级路径就最前面不要加/了 子路由 path 不加 /路径会自动拼接父路由路径保持嵌套关系正确用法。 子路由 path 加 /路径被视为绝对路径脱离父路由成为独立的一级路由不符合二级路由的设计意图。 3. 获取当前用户信息 3.1 数据库修改 给数据库添加用户昵称字段 nick_name varchar(20) not null comment 昵称,要么重新创建数据库 要么用alter alter table tb_sys_user add nick_name varchar(20) null after user_account ;update tb_sys_user set nick_name 超级管理员 where user_account aaa[HY000][1366] Incorrect string value: \xE8\xB6\x85\xE7\xBA\xA7... for column nick_name at row 1在update的时候报错了这个是因为不支持中文的原因 就是编码出问题 改一下配置文件就可以了 找到etc/my.cnf 加上配置 character-set-serverutf8mb4 collation-server utf8mb4_general_ci保存一下然后重启容器生效 但是就算这样修改了执行还是不行因为这个表提前就创建好了编码已经确定了 所以不行 所以我们要重新创建一个表 但是就算创建一个新的表还是有编码问题 怎么回事呢 因为数据库的编码没有变 得创建一个新的库才可以 我们先用root用户创建新的库 所以说改了配置文件以后删除以前的库才可以或者创建新的库才可以生效 右键表的数据然后生成sql生成insertSQL就可以保存数据了 这样就成功了 3.2 设计 3.3 开发后端 GetMapping(/info)public RLoginUserVO info(RequestHeader(HttpConstants.AUTHENTICATIO) String token){return sysUserService.info(token);}我们的token直接从header里面获取就可以了用的是RequestHeader注解 Data public class LoginUserVO {private String nickName; }Data public class LoginUser {//存储在redis中的用户信息private Integer identity;private String nickName; }这里也要完善一下存储到redis中的基本数据还有数据库对应的类也要增加字段记得还要修改对应的代码登录存储基本用户数据的时候记得修改代码存储昵称 tokenService里面分装方法 public LoginUser getLoginUser(String token, String secret ) {String userKey getUserKey(token, secret);if(userKey null){return null;}String tokenKey getTokenKey(userKey);return redisService.getCacheObject(tokenKey, LoginUser.class);}private String getTokenKey(String userKey) {return CacheConstants.LOGIN_TOKEN_KEY userKey;}private String getUserKey(String token, String secret) {Claims claims;try {claims JwtUtils.parseToken(token, secret); //获取令牌中信息 解析payload中信息if (claims null) {log.error(令牌已过期或验证不正确);return null;}} catch (Exception e) {log.error(令牌已过期或验证不正确e:,e);return null;}return JwtUtils.getUserKey(claims); //获取jwt中的key}Overridepublic RLoginUserVO info(String token) {if (StrUtil.isNotEmpty(token) token.startsWith(HttpConstants.PREFIX)) {token token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);}LoginUser loginUser tokenService.getLoginUser(token,secret);if(loginUser null){return R.fail();}LoginUserVO loginUserVO new LoginUserVO();loginUserVO.setNickName(loginUser.getNickName());return R.ok(loginUserVO);}然后测试一下 delkey可以删除redis数据 这样就成功了 这个是根据登录设置的header自动进行查询的不用传json 然后测试一下延长redis时间的接口也是没有问题的 3.4 开发前端 export function getUserInfoService(){return service({url: /sysUser/info,method: get}) }import { reactive } from vue; import { getUserInfoService } from /apis/suser;const loginUser reactive({nickName: })async function getUserInfo(){const userInfo await getUserInfoService();loginUser.nickName userInfo.data.nickName; }await getUserInfo();然后再request.js里面定义请求拦截器 //请求拦截器 service.interceptors.request.use((config) {if (getToken()) {config.headers[Authorization] Bearer getToken();}return config;},(error) {console.log(error)Promise.reject(error);} );这个请求拦截器就是拦截每个给后端发起的请求然后判断是否有token有的话就在请求头中加上token divstrong当前用户/strong{{loginUser.nickName}}/div注意登录的前端接口写错了 改一下为这个样子 async function loginFun() {try{const res await loginService(userAccount.value, password.value);setToken(res.data)router.push(/oj/layout)console.log(登录成功 , res.data);}catch(err){console.log(登录失败 , err);} }这样就可以了 4. 退出登录 4.1 业务分析 就是让token不为空然后解析一下解析出来不能执行正常业务了。是可以进行解析的 所以点击退出登录让redis中的数据不存在就可以了 这样就可以避免多次登录redis中的数据增多了 后端返回请求以后如果是成功的前端就清楚存储的token 所以就是后端清楚redis前端清楚token 4.2 后端开发 DeleteMapping(/logout)Operation(summary 退出登录, description 退出登录)public RVoid logout(RequestHeader(HttpConstants.AUTHENTICATION) String token){log.info(退出登录...,token:{}, token);return toR(sysUserService.logout(token));}因为退出登录后端会删除redis所以是DeleteMapping Overridepublic boolean logout(String token) {if (StrUtil.isNotEmpty(token) token.startsWith(HttpConstants.PREFIX)) {token token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);}return tokenService.deleteLoginUser(token,secret);}public boolean deleteLoginUser(String token, String secret) {String userKey getUserKey(token, secret);if(userKey null){return false;}String tokenKey getTokenKey(userKey);return redisService.deleteObject(tokenKey);}然后测试一下 4.3 前端开发 点击退出登录的时候会有一个消息弹窗 我们用的就是elementplus的消息弹窗 观察一下我们可以发现 ElMessageBox.confirm 方法返回一个 Promise 对象。当用户点击确认按钮时Promise 会进入 resolved 状态此时会执行 .then() 中的回调函数而当用户点击取消按钮或者关闭对话框时Promise 会进入 rejected 状态这时就会执行 .catch() 中的回调函数。 所以说用户点击取消按钮或者关闭对话框时就是相当于抛出了一个异常所以弹窗如果后面还有代码就不会执行了 而点击了确定按钮的话其实点击什么都是返回Promise 会返回一个 Promise 对象返回这个对象是一个异步的过程所以要await,不然异步的话就去判断Promise 对象可能会判断失误 所以说点击了确定按钮的话就会执行弹窗后面的代码了 export function logoutService(){return service({url: /sysUser/logout,method: delete}) }如果是函数抛出异常也会结束后面代码执行 async function logout(){await ElMessageBox.confirm(退出登录,温馨提示,{confirmButtonText: 确认,cancelButtonText: 取消,type: warning,})await logoutService();removeToken();router.push(/oj/login); }因为logoutService抛出的异常我们可以直接输出错误对于异常的情况没有什么好处理的就什么都不干就可以了所以我们不用try和catch 5. 前端路由优化 5.1 重定向 这个的问题是什么呢 就是我们点击这个地址不用直接到login而是要手动输入地址才可以了 什么做到点击这个http://localhost:5173/就可以自动跳转到login呢这个就要使用重定向了 {path: /,redirect: /oj/login},这样配置就可以了 5.2 全局前置守卫 我们要求 未登录要求不管点击哪个页面都要跳回登录页面 登录过后未过期点回login自动跳转功能页面 登录过后点击login就直接不用登录就可以使用功能了 就是要在路由跳转之前进行判断处理 谁来进行页面跳转呢就是routerrouter在哪里呢就是在index.js里面配置的所以对router进行配置即可就是对页面跳转之前进行的配置 router.beforeEach((to, from, next) {if (getToken()) { //已经登陆过/* has token*/if (to.path /oj/login) {next({ path: /oj/layout/question })} else {next()}} else {if (to.path ! /oj/login) {next({path:/oj/login})} else {next()}} })这样就可以了 to是目的路由 from是源路由 next是真正的目的路由是去哪里 next()就是和to一样的 这样我们在没有登录的情况下输入http://localhost:5173/oj/layout 就会自动跳转为http://localhost:5173/oj/login 登录下输入http://localhost:5173/oj/login 就会自动变为http://localhost:5173/oj/layout/question 但是如果登录状态过期呢 这个也是和没有登录是一样的怎么判断呢前端是无法判断token是否过期的 5.3 token过期处理 boolean isLogin redisService.hasKey(getTokenKey(userKey));if (!isLogin) {return unauthorizedResponse(exchange, 登录状态已过期);}private MonoVoid unauthorizedResponse(ServerWebExchange exchange, Stringmsg) {log.error([鉴权异常处理]请求路径:{}, exchange.getRequest().getPath());return webFluxResponseWriter(exchange.getResponse(), msg,ResultCode.FAILED_UNAUTHORIZED.getCode());}FAILED_UNAUTHORIZED (3001, 未授权),我们可以这样处理 因为后端对于token过期会报错的就在网关中会报登录状态已过期会报3001的错误而且我们还有响应拦截器所以就可以在响应拦截器中进行处理了如果过期了就自动跳转到login 所以说登录状态由浏览器的token和token是否过期一起决定 我们可以去redis中删除数据手动弄为过期 因为过期了redis就会自动删除数据所以我们删除它就是模仿的过期 service.interceptors.response.use((res) {// 未设置状态码则默认成功状态const code res.data.code;const msg res.data.msg;if(code 3001){ElMessage.error(msg);router.push(/oj/login)removeToken();return Promise.reject(new Error(msg));}else if (code ! 1000) {ElMessage.error(msg);return Promise.reject(new Error(msg));} else {return Promise.resolve(res.data);}},(error) {return Promise.reject(error);} );为什么要removeToken呢因为已经过期了没用了但是不删掉的话就可以一直去layout页面 注意如果是刷新的话在全局前置守卫中to就是自身url而from则是根路径 这样就OK了 调试也没有错误 总结
http://www.pierceye.com/news/693174/

相关文章:

  • 企业商城网站 .networdpress模板作者怎样去除
  • 强生网站还要怎样做衡水网站推广的网络公司
  • 茂名建站公司南通长城建设集团有限公司网站
  • 网络平台怎么建立网站吗做暧暧视频网站安全吗
  • 免费域名x网站网站前期准备工作
  • 陕西网站建设公司排名智能优化网站
  • 做瞹瞹网站萍乡做网站的公司有哪些
  • 网站建设的类型有几种wordpress搜索返回页面内容
  • 建设网站备案与不备案区别招远建网站首选公司
  • 四川住房和城乡建设厅网站三类人员软文网站备案如何查询
  • 个人与企业签订网站开发合同网页制作教程实例
  • 做网站遇到竞争对手怎么办wordpress中文版邮件发送
  • 美橙互联旗下网站渐变网站
  • 做网站域名需要在哪里备案微信答题小程序
  • 购物网站页面布局个人站长做导航网站
  • wordpress 增强编辑器网站暂时关闭 seo
  • 重庆网站设计开发培训广西省住房和城乡建设厅官网
  • 购物网站模板免费下载网站排名快速提升工具
  • 中山制作网站的公司滨江区网站开发公司
  • 申请建设工作网站的函酒店网站建设方案策划方案
  • 宠物店网站模板你是网站设计有限公司的项目经理
  • 潍坊网站开发公司2018做网站还赚钱吗
  • 做化妆品网站怎样wordpress映射到外网访问
  • 关于加强门户网站建设爱客crm客户管理系统
  • 网站备案的是域名还是空间电子商务网站建设携程
  • 建设企业网站管理系统目的开发一个网站的费用
  • 网站开发和浏览器兼容问题软文广告案例分析
  • 更新网站的方法自贡网站建设哪家好
  • 沈阳网络建网站个人电子商务网站建设的总体目标
  • asp 大型网站开发优化公司治理结构