[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-public-poUfWrWc":3,"public-project-articles-poUfWrWc":17},{"id":4,"uuid":5,"project_id":6,"title":7,"content":8,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},535,"poUfWrWc",47,"12. Vue3管理系统开发个人信息、修改密码页面","## 当你集成完登录功能后会出现白屏\n\n为什么？\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2024\u002Fpng\u002F751015\u002F1729585414880-33a4bd38-f6e3-4a7d-b068-66c1155cb797.png)\n\n怎么解决？\n\n把默认的路由重定向到  \u002Flogin，让用户每次启动项目后重新登录\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2024\u002Fpng\u002F751015\u002F1729585559435-033e9132-f206-4e66-a1e3-8dc867608157.png)\n\n## 管理员相关的增删改查\n\n### admin 的 sql\n\n```sql\nCREATE TABLE `admin` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',\n  `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '账号',\n  `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',\n  `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',\n  `role` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='管理员信息';\n```\n\n### 后端接口\n\nentity、controller、service、mapper、mapper.xml\n\n### 前端页面\n\n页面、路由、菜单\n\n## 未找到接口错误\n\n1. 前端写错接口了\n2. 后端没重启\n\n## 系统错误\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2024\u002Fpng\u002F751015\u002F1729586421294-8b782fef-ab8a-4da8-9c00-ed4255b040a3.png)\n\n## 退出登录逻辑\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2024\u002Fpng\u002F751015\u002F1729587041110-24fec531-36be-4890-866c-cebd25f1a450.png)\n\n```vue\nconst logout = () => {\n  localStorage.removeItem('xm-pro-user')  \u002F\u002F 清除当前登录的用户缓存数据\n  location.href = '\u002Flogin'  \u002F\u002F 退出到登录页面\n}\n```\n\n## 管理员登录\n\n```java\n\u002F**\n * 管理员\u002F员工登录\n *\u002F\n@PostMapping(\"\u002Flogin\")\npublic Result login(@RequestBody Account account) {\n    Account result = null;\n    if (\"ADMIN\".equals(account.getRole())) {  \u002F\u002F 管理员登录\n        result = adminService.login(account);\n    } else if (\"EMP\".equals(account.getRole())) {\n        result = employeeService.login(account);\n    }\n    return Result.success(result);\n}\n```\n\n## 个人中心页面\n\n### 子组件发送请求更新父组件的数据\n\n```vue\nconst emit = defineEmits(['updateUser'])\n\u002F\u002F 更新缓存数据\nlocalStorage.setItem('xm-pro-user', JSON.stringify(data.form))\nemit('updateUser')\n```\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2024\u002Fpng\u002F751015\u002F1729588451104-b1b11e1c-7ac0-461f-8e8b-87781891113e.png)\n\n![](https:\u002F\u002Fcdn.nlark.com\u002Fyuque\u002F0\u002F2024\u002Fpng\u002F751015\u002F1729588461433-0be54582-b90d-4c2f-9ba5-9dbc12c4c732.png)\n\n```vue\nconst updateUser = () => {\n  data.user = JSON.parse(localStorage.getItem('xm-pro-user'))\n  }\n```\n\n### 个人信息页面源码\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv class=\"card\" style=\"width: 50%; padding: 40px 20px\">\n    \u003Cel-form ref=\"formRef\" :rules=\"data.rules\" :model=\"data.form\" label-width=\"80px\" style=\"padding-right: 40px; padding-top: 20px\">\n      \u003Cel-form-item label=\"账号\" prop=\"username\">\n        \u003Cel-input disabled v-model=\"data.form.username\" autocomplete=\"off\" placeholder=\"请输入账号\" \u002F>\n      \u003C\u002Fel-form-item>\n      \u003Cel-form-item label=\"名称\" prop=\"name\">\n        \u003Cel-input v-model=\"data.form.name\" autocomplete=\"off\" placeholder=\"请输入名称\" \u002F>\n      \u003C\u002Fel-form-item>\n      \u003Cdiv v-if=\"data.user.role === 'EMP'\">\n        \u003Cel-form-item label=\"性别\">\n          \u003Cel-radio-group v-model=\"data.form.sex\">\n            \u003Cel-radio value=\"男\" label=\"男\">\u003C\u002Fel-radio>\n            \u003Cel-radio value=\"女\" label=\"女\">\u003C\u002Fel-radio>\n          \u003C\u002Fel-radio-group>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"工号\" prop=\"no\">\n          \u003Cel-input disabled v-model=\"data.form.no\" autocomplete=\"off\"  placeholder=\"请输入工号\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"年龄\">\n          \u003Cel-input-number style=\"width: 180px\" :min=\"18\" v-model=\"data.form.age\" autocomplete=\"off\"  placeholder=\"请输入年龄\" \u002F>\n        \u003C\u002Fel-form-item>\n        \u003Cel-form-item label=\"个人介绍\">\n          \u003Cel-input :rows=\"3\" type=\"textarea\" v-model=\"data.form.description\" autocomplete=\"off\" placeholder=\"请输入个人介绍\" \u002F>\n        \u003C\u002Fel-form-item>\n      \u003C\u002Fdiv>\n      \u003Cdiv style=\"text-align: center\">\n        \u003Cel-button @click=\"updateUser\" type=\"primary\" style=\"padding: 20px 30px\">更新个人信息\u003C\u002Fel-button>\n      \u003C\u002Fdiv>\n    \u003C\u002Fel-form>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { reactive, ref } from \"vue\";\nimport request from \"@\u002Futils\u002Frequest.js\";\nimport {ElMessage} from \"element-plus\";\n\nconst formRef = ref()\nconst data = reactive({\n  user: JSON.parse(localStorage.getItem('xm-pro-user')),\n  form: {},\n  rules: {\n    username: [\n      { required: true, message: '请输入账号', trigger: 'blur' }\n    ],\n    name: [\n      { required: true, message: '请输入名称', trigger: 'blur' }\n    ],\n    no: [\n      { required: true, message: '请输入工号', trigger: 'blur' }\n    ]\n  }\n})\n\nconst emit = defineEmits(['updateUser'])\n\nif (data.user.role === 'EMP') {\n  request.get('\u002Femployee\u002FselectById\u002F' + data.user.id).then(res => {\n    data.form = res.data\n  })\n} else {\n  data.form = data.user\n}\n\nconst updateUser = () => {\n  if (data.user.role === 'EMP') {\n    request.put('\u002Femployee\u002Fupdate', data.form).then(res => {\n      if (res.code === '200') {\n        ElMessage.success('更新成功')\n        \u002F\u002F 更新缓存数据\n        localStorage.setItem('xm-pro-user', JSON.stringify(data.form))\n        \u002F\u002F 触发父级从缓存里面取到最新的数据\n        emit('updateUser')\n      } else {\n        ElMessage.error(res.msg)\n      }\n    })\n  } else {\n    request.put('\u002Fadmin\u002Fupdate', data.form).then(res => {\n      if (res.code === '200') {\n        ElMessage.success('更新成功')\n        \u002F\u002F 更新缓存数据\n        localStorage.setItem('xm-pro-user', JSON.stringify(data.form))\n        \u002F\u002F 触发父级从缓存里面取到最新的数据\n        emit('updateUser')\n      } else {\n        ElMessage.error(res.msg)\n      }\n    })\n  }\n}\n\u003C\u002Fscript>\n```\n\n## 修改密码\n\n```java\n\u002F**\n * 修改密码\n *\u002F\n@PutMapping(\"\u002FupdatePassword\")\npublic Result updatePassword(@RequestBody Account account) {\n    if (\"ADMIN\".equals(account.getRole())) {  \u002F\u002F 管理员登录\n        adminService.updatePassword(account);\n    } else if (\"EMP\".equals(account.getRole())) {\n        employeeService.updatePassword(account);\n    }\n    return Result.success();\n}\n```\n\n```vue\npublic void updatePassword(Account account) {\n    Integer id = account.getId();\n    Admin admin = this.selectById(id);\n    if (!admin.getPassword().equals(account.getPassword())) {  \u002F\u002F 页面传来的原密码跟数据库密码对比  不匹配就报错\n        throw new CustomException(\"500\", \"对不起，原密码错误\");\n    }\n    admin.setPassword(account.getNewPassword());  \u002F\u002F 设置新密码\n    this.update(admin);\n}\n```\n\n修改密码的前端页面\n\n```vue\n\u003Ctemplate>\n  \u003Cdiv class=\"card\" style=\"width: 50%; padding: 40px 20px;\">\n    \u003Cel-form ref=\"formRef\" :rules=\"data.rules\" :model=\"data.form\" label-width=\"100px\" style=\"padding-right: 40px; padding-top: 20px\">\n      \u003Cel-form-item label=\"原密码\" prop=\"password\">\n        \u003Cel-input show-password v-model=\"data.form.password\" autocomplete=\"off\" placeholder=\"请输入原密码\" \u002F>\n      \u003C\u002Fel-form-item>\n      \u003Cel-form-item label=\"新密码\" prop=\"newPassword\">\n        \u003Cel-input show-password v-model=\"data.form.newPassword\" autocomplete=\"off\" placeholder=\"请输入新密码\" \u002F>\n      \u003C\u002Fel-form-item>\n      \u003Cel-form-item label=\"确认新密码\" prop=\"confirmPassword\" required>\n        \u003Cel-input show-password v-model=\"data.form.confirmPassword\" autocomplete=\"off\" placeholder=\"请再次确认新密码\" \u002F>\n      \u003C\u002Fel-form-item>\n      \u003Cdiv style=\"text-align: center\">\n        \u003Cel-button @click=\"updatePassword\" type=\"primary\" style=\"padding: 20px 30px\">立即修改\u003C\u002Fel-button>\n      \u003C\u002Fdiv>\n    \u003C\u002Fel-form>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport {reactive, ref} from \"vue\";\nimport request from \"@\u002Futils\u002Frequest.js\";\nimport {ElMessage} from \"element-plus\";\n\nconst validatePass = (rule, value, callback) => {\n  if (!value) {\n    callback(new Error('请再次确认新密码'))\n  } else if (value !== data.form.newPassword) {\n    callback(new Error(\"两次输入的密码不一致\"))\n  } else {\n    callback()\n  }\n}\n\nconst formRef = ref()\nconst data = reactive({\n  user: JSON.parse(localStorage.getItem('xm-pro-user')),\n  form: {},\n  rules: {\n    password: [\n      {required: true, message: '请输入原密码', trigger: 'blur'}\n    ],\n    newPassword: [\n      {required: true, message: '请输入新密码', trigger: 'blur'}\n    ],\n    confirmPassword: [\n      { validator: validatePass, trigger: 'blur'}\n    ],\n  }\n})\n\nconst updatePassword = () => {\n  data.form.id = data.user.id\n  data.form.role = data.user.role\n  formRef.value.validate((valid) => {\n    if (valid) {\n      request.put('\u002FupdatePassword', data.form).then(res => {\n        if (res.code === '200') {\n          ElMessage.success('修改成功')\n          localStorage.removeItem('xm-pro-user')\n          setTimeout(() => {\n            location.href = '\u002Flogin'\n          }, 500)\n        } else {\n          ElMessage.error(res.msg)\n        }\n      })\n    }\n  })\n}\n\u003C\u002Fscript>\n```\n\n","coding",1,5777,1050,"2024-10-22 17:50:30","2026-05-03 22:49:02","1天学会SpringBoot3+Vue3实战项目开发","learn-springboot-vue",{"project":18,"items":19},{"id":6,"title":15,"slug":16},[20,27,34,42,49,56,63,70,77,84,91,98,99,106,113,120],{"id":21,"uuid":22,"project_id":6,"title":23,"type":9,"status":10,"public_enabled":10,"views":24,"sort":25,"created_at":26,"updated_at":14,"project_title":15,"project_slug":16},475,"OgrBbww7","01. 1天学会SpringBoot3+Vue3实战项目课程介绍",17862,910,"2024-10-11 16:50:50",{"id":28,"uuid":29,"project_id":6,"title":30,"type":9,"status":10,"public_enabled":10,"views":31,"sort":32,"created_at":33,"updated_at":14,"project_title":15,"project_slug":16},476,"U58ISSFR","02. 从0带你搭建Vue3工程",12899,911,"2024-10-11 16:47:49",{"id":35,"uuid":36,"project_id":6,"title":37,"type":9,"status":10,"public_enabled":10,"views":38,"sort":39,"created_at":40,"updated_at":41,"project_title":15,"project_slug":16},478,"tK3YUYq8","03. Vue3集成Element-Plus",9899,919,"2024-10-14 22:18:17","2026-05-07 15:33:28.189425+00",{"id":43,"uuid":44,"project_id":6,"title":45,"type":9,"status":10,"public_enabled":10,"views":46,"sort":47,"created_at":48,"updated_at":14,"project_title":15,"project_slug":16},488,"J2MV6UAG","04. Element-Plus组件使用速成",8093,938,"2024-10-11 16:49:40",{"id":50,"uuid":51,"project_id":6,"title":52,"type":9,"status":10,"public_enabled":10,"views":53,"sort":54,"created_at":55,"updated_at":14,"project_title":15,"project_slug":16},489,"zGi4XJsb","05. Vue3集成Vue-Router实现路由跳转",7443,939,"2024-10-12 15:44:41",{"id":57,"uuid":58,"project_id":6,"title":59,"type":9,"status":10,"public_enabled":10,"views":60,"sort":61,"created_at":62,"updated_at":14,"project_title":15,"project_slug":16},499,"cwsTdvo9","06. Vue3搭建后台管理系统",7421,964,"2024-10-14 16:02:49",{"id":64,"uuid":65,"project_id":6,"title":66,"type":9,"status":10,"public_enabled":10,"views":67,"sort":68,"created_at":69,"updated_at":14,"project_title":15,"project_slug":16},501,"JlXltpKA","07. Mysql语法简介（速成）",5208,973,"2024-10-15 16:52:18",{"id":71,"uuid":72,"project_id":6,"title":73,"type":9,"status":10,"public_enabled":10,"views":74,"sort":75,"created_at":76,"updated_at":14,"project_title":15,"project_slug":16},509,"c3XrOTcU","08. 从0带你搭建SpringBoot3工程",9397,992,"2024-10-16 15:43:13",{"id":78,"uuid":79,"project_id":6,"title":80,"type":9,"status":10,"public_enabled":10,"views":81,"sort":82,"created_at":83,"updated_at":14,"project_title":15,"project_slug":16},516,"QsKwbDX7","09. SpringBoot3集成Mybatis",10348,1004,"2024-10-17 16:36:15",{"id":85,"uuid":86,"project_id":6,"title":87,"type":9,"status":10,"public_enabled":10,"views":88,"sort":89,"created_at":90,"updated_at":14,"project_title":15,"project_slug":16},519,"qKG13ySo","10. SpringBoot3+Vue3实现基本的增删改查功能",9572,1013,"2024-10-18 16:34:55",{"id":92,"uuid":93,"project_id":6,"title":94,"type":9,"status":10,"public_enabled":10,"views":95,"sort":96,"created_at":97,"updated_at":14,"project_title":15,"project_slug":16},527,"7uED9n7e","11. Vue3开发登录注册页面",7106,1031,"2024-10-21 17:35:30",{"id":4,"uuid":5,"project_id":6,"title":7,"type":9,"status":10,"public_enabled":10,"views":11,"sort":12,"created_at":13,"updated_at":14,"project_title":15,"project_slug":16},{"id":100,"uuid":101,"project_id":6,"title":102,"type":9,"status":10,"public_enabled":10,"views":103,"sort":104,"created_at":105,"updated_at":14,"project_title":15,"project_slug":16},542,"FxHR3hNR","13. SpringBoot3+Vue3实现文件上传下载功能",4489,1057,"2024-10-23 17:31:02",{"id":107,"uuid":108,"project_id":6,"title":109,"type":9,"status":10,"public_enabled":10,"views":110,"sort":111,"created_at":112,"updated_at":14,"project_title":15,"project_slug":16},548,"VPZNSTxr","14. SpringBoot3+Vue3实现富文本编辑器功能",3931,1072,"2024-10-24 17:38:21",{"id":114,"uuid":115,"project_id":6,"title":116,"type":9,"status":10,"public_enabled":10,"views":117,"sort":118,"created_at":119,"updated_at":14,"project_title":15,"project_slug":16},555,"nal0yVxM","15. SpringBoot3+Vue3实现数据批量导入导出功能",3577,1090,"2024-10-28 17:39:21",{"id":121,"uuid":122,"project_id":6,"title":123,"type":9,"status":10,"public_enabled":10,"views":124,"sort":125,"created_at":126,"updated_at":14,"project_title":15,"project_slug":16},564,"XxSPPFGi","16. SpringBoot3+Vue3实现数据统计图表功能",4236,1109,"2025-01-09 09:40:57"]